1# @file CharEncodingCheck.py 2# 3# Copyright (c) Microsoft Corporation. 4# SPDX-License-Identifier: BSD-2-Clause-Patent 5## 6 7 8import os 9import logging 10from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin 11from edk2toolext.environment.var_dict import VarDict 12 13## 14# map 15## 16EcodingMap = { 17 ".md": 'utf-8', 18 ".dsc": 'utf-8', 19 ".dec": 'utf-8', 20 ".c": 'utf-8', 21 ".h": 'utf-8', 22 ".asm": 'utf-8', 23 ".masm": 'utf-8', 24 ".nasm": 'utf-8', 25 ".s": 'utf-8', 26 ".inf": 'utf-8', 27 ".asl": 'utf-8', 28 ".uni": 'utf-8', 29 ".py": 'utf-8' 30} 31 32 33class CharEncodingCheck(ICiBuildPlugin): 34 """ 35 A CiBuildPlugin that scans each file in the code tree and confirms the encoding is correct. 36 37 Configuration options: 38 "CharEncodingCheck": { 39 "IgnoreFiles": [] 40 } 41 """ 42 43 def GetTestName(self, packagename: str, environment: VarDict) -> tuple: 44 """ Provide the testcase name and classname for use in reporting 45 testclassname: a descriptive string for the testcase can include whitespace 46 classname: should be patterned <packagename>.<plugin>.<optionally any unique condition> 47 48 Args: 49 packagename: string containing name of package to build 50 environment: The VarDict for the test to run in 51 Returns: 52 a tuple containing the testcase name and the classname 53 (testcasename, classname) 54 """ 55 return ("Check for valid file encoding for " + packagename, packagename + ".CharEncodingCheck") 56 57 ## 58 # External function of plugin. This function is used to perform the task of the ci_build_plugin Plugin 59 # 60 # - package is the edk2 path to package. This means workspace/packagepath relative. 61 # - edk2path object configured with workspace and packages path 62 # - PkgConfig Object (dict) for the pkg 63 # - EnvConfig Object 64 # - Plugin Manager Instance 65 # - Plugin Helper Obj Instance 66 # - Junit Logger 67 # - output_stream the StringIO output stream from this plugin via logging 68 def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream=None): 69 overall_status = 0 70 files_tested = 0 71 72 abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(packagename) 73 74 if abs_pkg_path is None: 75 tc.SetSkipped() 76 tc.LogStdError("No Package folder {0}".format(abs_pkg_path)) 77 return 0 78 79 for (ext, enc) in EcodingMap.items(): 80 files = self.WalkDirectoryForExtension([ext], abs_pkg_path) 81 files = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(x) for x in files] # make edk2relative path so can process ignores 82 83 if "IgnoreFiles" in pkgconfig: 84 for a in pkgconfig["IgnoreFiles"]: 85 a = a.replace(os.sep, "/") 86 try: 87 tc.LogStdOut("Ignoring File {0}".format(a)) 88 files.remove(a) 89 except: 90 tc.LogStdError("CharEncodingCheck.IgnoreInf -> {0} not found in filesystem. Invalid ignore file".format(a)) 91 logging.info("CharEncodingCheck.IgnoreInf -> {0} not found in filesystem. Invalid ignore file".format(a)) 92 93 files = [Edk2pathObj.GetAbsolutePathOnThisSytemFromEdk2RelativePath(x) for x in files] 94 for a in files: 95 files_tested += 1 96 if(self.TestEncodingOk(a, enc)): 97 logging.debug("File {0} Passed Encoding Check {1}".format(a, enc)) 98 else: 99 tc.LogStdError("Encoding Failure in {0}. Not {1}".format(a, enc)) 100 overall_status += 1 101 102 tc.LogStdOut("Tested Encoding on {0} files".format(files_tested)) 103 if overall_status is not 0: 104 tc.SetFailed("CharEncoding {0} Failed. Errors {1}".format(packagename, overall_status), "CHAR_ENCODING_CHECK_FAILED") 105 else: 106 tc.SetSuccess() 107 return overall_status 108 109 def TestEncodingOk(self, apath, encodingValue): 110 try: 111 with open(apath, "rb") as fobj: 112 fobj.read().decode(encodingValue) 113 except Exception as exp: 114 logging.error("Encoding failure: file: {0} type: {1}".format(apath, encodingValue)) 115 logging.debug("EXCEPTION: while processing {1} - {0}".format(exp, apath)) 116 return False 117 118 return True 119