# @file CharEncodingCheck.py | |
# | |
# Copyright (c) Microsoft Corporation. | |
# SPDX-License-Identifier: BSD-2-Clause-Patent | |
## | |
import os | |
import logging | |
from edk2toolext.environment.plugintypes.ci_build_plugin import ICiBuildPlugin | |
from edk2toolext.environment.var_dict import VarDict | |
## | |
# map | |
## | |
EcodingMap = { | |
".md": 'utf-8', | |
".dsc": 'utf-8', | |
".dec": 'utf-8', | |
".c": 'utf-8', | |
".h": 'utf-8', | |
".asm": 'utf-8', | |
".masm": 'utf-8', | |
".nasm": 'utf-8', | |
".s": 'utf-8', | |
".inf": 'utf-8', | |
".asl": 'utf-8', | |
".uni": 'utf-8', | |
".py": 'utf-8' | |
} | |
class CharEncodingCheck(ICiBuildPlugin): | |
""" | |
A CiBuildPlugin that scans each file in the code tree and confirms the encoding is correct. | |
Configuration options: | |
"CharEncodingCheck": { | |
"IgnoreFiles": [] | |
} | |
""" | |
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 valid file encoding for " + packagename, packagename + ".CharEncodingCheck") | |
## | |
# 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): | |
overall_status = 0 | |
files_tested = 0 | |
abs_pkg_path = Edk2pathObj.GetAbsolutePathOnThisSystemFromEdk2RelativePath(packagename) | |
if abs_pkg_path is None: | |
tc.SetSkipped() | |
tc.LogStdError("No Package folder {0}".format(abs_pkg_path)) | |
return 0 | |
for (ext, enc) in EcodingMap.items(): | |
files = self.WalkDirectoryForExtension([ext], abs_pkg_path) | |
files = [Edk2pathObj.GetEdk2RelativePathFromAbsolutePath(x) for x in files] # make edk2relative path so can process ignores | |
if "IgnoreFiles" in pkgconfig: | |
for a in pkgconfig["IgnoreFiles"]: | |
a = a.replace(os.sep, "/") | |
try: | |
tc.LogStdOut("Ignoring File {0}".format(a)) | |
files.remove(a) | |
except: | |
tc.LogStdError("CharEncodingCheck.IgnoreInf -> {0} not found in filesystem. Invalid ignore file".format(a)) | |
logging.info("CharEncodingCheck.IgnoreInf -> {0} not found in filesystem. Invalid ignore file".format(a)) | |
files = [Edk2pathObj.GetAbsolutePathOnThisSystemFromEdk2RelativePath(x) for x in files] | |
for a in files: | |
files_tested += 1 | |
if not self.TestEncodingOk(a, enc): | |
tc.LogStdError("Encoding Failure in {0}. Not {1}".format(a, enc)) | |
overall_status += 1 | |
tc.LogStdOut("Tested Encoding on {0} files".format(files_tested)) | |
if overall_status != 0: | |
tc.SetFailed("CharEncoding {0} Failed. Errors {1}".format(packagename, overall_status), "CHAR_ENCODING_CHECK_FAILED") | |
else: | |
tc.SetSuccess() | |
return overall_status | |
def TestEncodingOk(self, apath, encodingValue): | |
try: | |
with open(apath, "rb") as fobj: | |
fobj.read().decode(encodingValue) | |
except Exception as exp: | |
logging.error("Encoding failure: file: {0} type: {1}".format(apath, encodingValue)) | |
logging.debug("EXCEPTION: while processing {1} - {0}".format(exp, apath)) | |
return False | |
return True |