]> git.proxmox.com Git - mirror_edk2.git/blob - .pytool/Plugin/LicenseCheck/LicenseCheck.py
.pytool/Plugin: Add a plugin LicenseCheck
[mirror_edk2.git] / .pytool / Plugin / LicenseCheck / LicenseCheck.py
1 # @file LicenseCheck.py
2 #
3 # Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
4 # SPDX-License-Identifier: BSD-2-Clause-Patent
5 ##
6
7 import os
8 import logging
9 import re
10 from io import StringIO
11 from typing import List, Tuple
12 from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin
13 from edk2toolext.environment.var_dict import VarDict
14 from edk2toollib.utility_functions import RunCmd
15
16
17 class LicenseCheck(ICiBuildPlugin):
18
19 """
20 A CiBuildPlugin to check the license for new added files.
21
22 Configuration options:
23 "LicenseCheck": {
24 "IgnoreFiles": []
25 },
26 """
27
28 license_format_preflix = 'SPDX-License-Identifier'
29
30 bsd2_patent = 'BSD-2-Clause-Patent'
31
32 Readdedfileformat = re.compile(r'\+\+\+ b\/(.*)')
33
34 file_extension_list = [".c", ".h", ".inf", ".dsc", ".dec", ".py", ".bat", ".sh", ".uni", ".yaml",
35 ".fdf", ".inc", "yml", ".asm", ".asm16", ".asl", ".vfr", ".s", ".S", ".aslc",
36 ".nasm", ".nasmb", ".idf", ".Vfr", ".H"]
37
38 def GetTestName(self, packagename: str, environment: VarDict) -> tuple:
39 """ Provide the testcase name and classname for use in reporting
40 testclassname: a descriptive string for the testcase can include whitespace
41 classname: should be patterned <packagename>.<plugin>.<optionally any unique condition>
42
43 Args:
44 packagename: string containing name of package to build
45 environment: The VarDict for the test to run in
46 Returns:
47 a tuple containing the testcase name and the classname
48 (testcasename, classname)
49 """
50 return ("Check for license for " + packagename, packagename + ".LicenseCheck")
51
52 ##
53 # External function of plugin. This function is used to perform the task of the ci_build_plugin Plugin
54 #
55 # - package is the edk2 path to package. This means workspace/packagepath relative.
56 # - edk2path object configured with workspace and packages path
57 # - PkgConfig Object (dict) for the pkg
58 # - EnvConfig Object
59 # - Plugin Manager Instance
60 # - Plugin Helper Obj Instance
61 # - Junit Logger
62 # - output_stream the StringIO output stream from this plugin via logging
63 def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None):
64 return_buffer = StringIO()
65 params = "diff --unified=0 origin/master HEAD"
66 RunCmd("git", params, outstream=return_buffer)
67 p = return_buffer.getvalue().strip()
68 patch = p.split("\n")
69 return_buffer.close()
70
71 ignore_files = []
72 if "IgnoreFiles" in pkgconfig:
73 ignore_files = pkgconfig["IgnoreFiles"]
74
75 self.ok = True
76 self.startcheck = False
77 self.license = True
78 self.all_file_pass = True
79 count = len(patch)
80 line_index = 0
81 for line in patch:
82 if line.startswith('--- /dev/null'):
83 nextline = patch[line_index + 1]
84 added_file = self.Readdedfileformat.search(nextline).group(1)
85 added_file_extension = os.path.splitext(added_file)[1]
86 if added_file_extension in self.file_extension_list and packagename in added_file:
87 if (self.IsIgnoreFile(added_file, ignore_files)):
88 line_index = line_index + 1
89 continue
90 self.startcheck = True
91 self.license = False
92 if self.startcheck and self.license_format_preflix in line:
93 if self.bsd2_patent in line:
94 self.license = True
95 if line_index + 1 == count or patch[line_index + 1].startswith('diff --') and self.startcheck:
96 if not self.license:
97 self.all_file_pass = False
98 error_message = "Invalid license in: " + added_file + " Hint: Only BSD-2-Clause-Patent is accepted."
99 logging.error(error_message)
100 self.startcheck = False
101 self.license = True
102 line_index = line_index + 1
103
104 if self.all_file_pass:
105 tc.SetSuccess()
106 return 0
107 else:
108 tc.SetFailed("License Check {0} Failed. ".format(packagename), "LICENSE_CHECK_FAILED")
109 return 1
110
111 def IsIgnoreFile(self, file: str, ignore_files: List[str]) -> bool:
112 for f in ignore_files:
113 if f in file:
114 return True
115 return False