| # @file LicenseCheck.py | |
| # | |
| # Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> | |
| # SPDX-License-Identifier: BSD-2-Clause-Patent | |
| ## | |
| import os | |
| import shutil | |
| import logging | |
| import re | |
| from io import StringIO | |
| from typing import List, Tuple | |
| from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin | |
| from edk2toolext.environment.var_dict import VarDict | |
| from edk2toollib.utility_functions import RunCmd | |
| class LicenseCheck(ICiBuildPlugin): | |
| """ | |
| A CiBuildPlugin to check the license for new added files. | |
| Configuration options: | |
| "LicenseCheck": { | |
| "IgnoreFiles": [] | |
| }, | |
| """ | |
| license_format_preflix = 'SPDX-License-Identifier' | |
| bsd2_patent = 'BSD-2-Clause-Patent' | |
| Readdedfileformat = re.compile(r'\+\+\+ b\/(.*)') | |
| file_extension_list = [".c", ".h", ".inf", ".dsc", ".dec", ".py", ".bat", ".sh", ".uni", ".yaml", | |
| ".fdf", ".inc", "yml", ".asm", ".asm16", ".asl", ".vfr", ".s", ".S", ".aslc", | |
| ".nasm", ".nasmb", ".idf", ".Vfr", ".H"] | |
| def GetTestName(self, packagename: str, environment: VarDict) -> tuple: | |
| """ Provide the testcase name and classname for use in reporting | |
| testclassname: a descriptive string for the testcase can include whitespace | |
| classname: should be patterned <packagename>.<plugin>.<optionally any unique condition> | |
| Args: | |
| packagename: string containing name of package to build | |
| environment: The VarDict for the test to run in | |
| Returns: | |
| a tuple containing the testcase name and the classname | |
| (testcasename, classname) | |
| """ | |
| return ("Check for license for " + packagename, packagename + ".LicenseCheck") | |
| ## | |
| # External function of plugin. This function is used to perform the task of the ci_build_plugin Plugin | |
| # | |
| # - package is the edk2 path to package. This means workspace/packagepath relative. | |
| # - edk2path object configured with workspace and packages path | |
| # - PkgConfig Object (dict) for the pkg | |
| # - EnvConfig Object | |
| # - Plugin Manager Instance | |
| # - Plugin Helper Obj Instance | |
| # - Junit Logger | |
| # - output_stream the StringIO output stream from this plugin via logging | |
| def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None): | |
| # Create temp directory | |
| temp_path = os.path.join(Edk2pathObj.WorkspacePath, 'Build', '.pytool', 'Plugin', 'LicenseCheck') | |
| if not os.path.exists(temp_path): | |
| os.makedirs(temp_path) | |
| # Output file to use for git diff operations | |
| temp_diff_output = os.path.join (temp_path, 'diff.txt') | |
| params = "diff --output={} --unified=0 origin/master HEAD".format(temp_diff_output) | |
| RunCmd("git", params) | |
| with open(temp_diff_output) as file: | |
| patch = file.read().strip().split("\n") | |
| # Delete temp directory | |
| if os.path.exists(temp_path): | |
| shutil.rmtree(temp_path) | |
| ignore_files = [] | |
| if "IgnoreFiles" in pkgconfig: | |
| ignore_files = pkgconfig["IgnoreFiles"] | |
| self.ok = True | |
| self.startcheck = False | |
| self.license = True | |
| self.all_file_pass = True | |
| count = len(patch) | |
| line_index = 0 | |
| for line in patch: | |
| if line.startswith('--- /dev/null'): | |
| nextline = patch[line_index + 1] | |
| added_file = self.Readdedfileformat.search(nextline).group(1) | |
| added_file_extension = os.path.splitext(added_file)[1] | |
| if added_file_extension in self.file_extension_list and packagename in added_file: | |
| if (self.IsIgnoreFile(added_file, ignore_files)): | |
| line_index = line_index + 1 | |
| continue | |
| self.startcheck = True | |
| self.license = False | |
| if self.startcheck and self.license_format_preflix in line: | |
| if self.bsd2_patent in line: | |
| self.license = True | |
| if line_index + 1 == count or patch[line_index + 1].startswith('diff --') and self.startcheck: | |
| if not self.license: | |
| self.all_file_pass = False | |
| error_message = "Invalid license in: " + added_file + " Hint: Only BSD-2-Clause-Patent is accepted." | |
| logging.error(error_message) | |
| self.startcheck = False | |
| self.license = True | |
| line_index = line_index + 1 | |
| if self.all_file_pass: | |
| tc.SetSuccess() | |
| return 0 | |
| else: | |
| tc.SetFailed("License Check {0} Failed. ".format(packagename), "LICENSE_CHECK_FAILED") | |
| return 1 | |
| def IsIgnoreFile(self, file: str, ignore_files: List[str]) -> bool: | |
| for f in ignore_files: | |
| if f in file: | |
| return True | |
| return False |