Sync scripts with Arm internal CI

This patch syncs utility scripts and scripts
in the script directory with the internal CI.

Where a path update is required,
the changes have been commented out.

Signed-off-by: Zelalem <zelalem.aweke@arm.com>
Change-Id: Ifa4bd805e345184d1378e8423e5f878a2fbfbcd4
diff --git a/script/static-checks/check-copyright.py b/script/static-checks/check-copyright.py
index 350381b..39863c7 100755
--- a/script/static-checks/check-copyright.py
+++ b/script/static-checks/check-copyright.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-# Copyright (c) 2019, Arm Limited. All rights reserved.
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -8,8 +8,10 @@
 """
 Check if a given file includes the copyright boiler plate.
 This checker supports the following comment styles:
-    * Used by .c, .h, .S, .dts and .dtsi files
-    # Used by Makefile (including .mk)
+    /*
+    *
+    //
+    #
 """
 
 import argparse
@@ -24,7 +26,9 @@
 from itertools import islice
 
 # File extensions to check
-VALID_FILE_EXTENSIONS = ('.c', '.S', '.h', 'Makefile', '.mk', '.dts', '.dtsi', '.ld')
+VALID_FILE_EXTENSIONS = ('.c', '.conf', '.dts', '.dtsi', '.editorconfig',
+                         '.h', '.i', '.ld', 'Makefile', '.mk', '.msvc',
+                         '.py', '.S', '.scat', '.sh')
 
 # Paths inside the tree to ignore. Hidden folders and files are always ignored.
 # They mustn't end in '/'.
@@ -41,47 +45,64 @@
 )
 
 # Supported comment styles (Python regex)
-COMMENT_PATTERN = '^(( \* ?)|(\# ?))'
+COMMENT_PATTERN = '(\*|/\*|\#|//)'
 
-# License pattern to match
-LICENSE_PATTERN = '''(?P<copyright_prologue>
-{0}Copyright \(c\) (?P<years>[0-9]{{4}}(-[0-9]{{4}})?), (Arm Limited|ARM Limited and Contributors)\. All rights reserved\.$
-{0}$
-{0}SPDX-License-Identifier: BSD-3-Clause$
-)'''.format(
-    COMMENT_PATTERN
-)
+# Any combination of spaces and/or tabs
+SPACING = '[ \t]*'
 
-# Compiled license pattern
-RE_PATTERN = re.compile(LICENSE_PATTERN, re.MULTILINE)
+# Line must start with a comment and optional spacing
+LINE_START = '^' + SPACING + COMMENT_PATTERN + SPACING
+
+# Line end with optional spacing
+EOL = SPACING + '$'
+
+# Year or period as YYYY or YYYY-YYYY
+TIME_PERIOD = '[0-9]{4}(-[0-9]{4})?'
+
+# Any string with valid license ID, don't allow adding postfix
+LICENSE_ID = '.*(BSD-3-Clause|BSD-2-Clause-FreeBSD)([ ,.\);].*)?'
+
+# File must contain both lines to pass the check
+COPYRIGHT_LINE = LINE_START + 'Copyright' + '.*' + TIME_PERIOD + '.*' + EOL
+LICENSE_ID_LINE = LINE_START + 'SPDX-License-Identifier:' + LICENSE_ID + EOL
+
+# Compiled license patterns
+COPYRIGHT_PATTERN = re.compile(COPYRIGHT_LINE, re.MULTILINE)
+LICENSE_ID_PATTERN = re.compile(LICENSE_ID_LINE, re.MULTILINE)
+
+CURRENT_YEAR = str(datetime.datetime.now().year)
 
 COPYRIGHT_OK = 0
 COPYRIGHT_ERROR = 1
-COPYRIGHT_WARNING = 2
 
-def check_copyright(path):
+def check_copyright(path, args, encoding='utf-8'):
     '''Checks a file for a correct copyright header.'''
 
-    with open(path) as file_:
+    result = COPYRIGHT_OK
+
+    with open(path, encoding=encoding) as file_:
         file_content = file_.read()
 
-    if RE_PATTERN.search(file_content):
-        return COPYRIGHT_OK
+    copyright_line = COPYRIGHT_PATTERN.search(file_content)
+    if not copyright_line:
+        print("ERROR: Missing copyright in " + file_.name)
+        result = COPYRIGHT_ERROR
+    elif CURRENT_YEAR not in copyright_line.group():
+        print("WARNING: Copyright is out of date in " + file_.name + ": '" +
+              copyright_line.group() + "'")
 
-    for line in file_content.split('\n'):
-        if 'SPDX-License-Identifier' in line:
-            if ('BSD-3-Clause' in line or
-                'BSD-2-Clause-FreeBSD' in line):
-                return COPYRIGHT_WARNING
-            break
+    if not LICENSE_ID_PATTERN.search(file_content):
+        print("ERROR: License ID error in " + file_.name)
+        result = COPYRIGHT_ERROR
 
-    return COPYRIGHT_ERROR
-
+    return result
 
 def main(args):
     print("Checking the copyrights in the code...")
 
-    all_files_correct = True
+    if args.verbose:
+        print ("Copyright regexp: " + COPYRIGHT_LINE)
+        print ("License regexp: " + LICENSE_ID_LINE)
 
     if args.patch:
         print("Checking files modified between patches " + args.from_ref
@@ -90,7 +111,7 @@
         (rc, stdout, stderr) = utils.shell_command(['git', 'diff',
             '--diff-filter=ACMRT', '--name-only', args.from_ref, args.to_ref ])
         if rc:
-            return 1
+            return COPYRIGHT_ERROR
 
         files = stdout.splitlines()
 
@@ -99,7 +120,7 @@
 
         (rc, stdout, stderr) = utils.shell_command([ 'git', 'ls-files' ])
         if rc:
-            return 1
+            return COPYRIGHT_ERROR
 
         files = stdout.splitlines()
 
@@ -117,30 +138,22 @@
         if args.verbose:
             print("Checking file " + f)
 
-        rc = check_copyright(f)
+        rc = check_copyright(f, args)
 
         if rc == COPYRIGHT_OK:
             count_ok += 1
-        elif rc == COPYRIGHT_WARNING:
-            count_warning += 1
-            print("WARNING: " + f)
         elif rc == COPYRIGHT_ERROR:
             count_error += 1
-            print("ERROR: " + f)
 
     print("\nSummary:")
-    print("\t{} files analyzed".format(count_ok + count_warning + count_error))
+    print("\t{} files analyzed".format(count_ok + count_error))
 
-    if count_warning == 0 and count_error == 0:
+    if count_error == 0:
         print("\tNo errors found")
-        return 0
-
-    if count_error > 0:
+        return COPYRIGHT_OK
+    else:
         print("\t{} errors found".format(count_error))
-
-    if count_warning > 0:
-        print("\t{} warnings found".format(count_warning))
-
+        return COPYRIGHT_ERROR
 
 def parse_cmd_line(argv, prog_name):
     parser = argparse.ArgumentParser(
@@ -166,9 +179,20 @@
 Instead of checking all files in the source tree, the script will consider
 only files that are modified by the latest patch(es).""",
                         action="store_true")
+
+    (rc, stdout, stderr) = utils.shell_command(['git', 'merge-base', 'HEAD', 'master'])
+    if rc:
+        print("Git merge-base command failed. Cannot determine base commit.")
+        sys.exit(rc)
+    merge_bases = stdout.splitlines()
+
+    # This should not happen, but it's better to be safe.
+    if len(merge_bases) > 1:
+        print("WARNING: Multiple merge bases found. Using the first one as base commit.")
+
     parser.add_argument("--from-ref",
                         help="Base commit in patch mode (default: %(default)s)",
-                        default="master")
+                        default=merge_bases[0])
     parser.add_argument("--to-ref",
                         help="Final commit in patch mode (default: %(default)s)",
                         default="HEAD")