Skip to content

Commit

Permalink
Add deprecation warning plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
NishanthSanjeevi authored and Javagedes committed Jul 24, 2024
1 parent d9c18c3 commit 76157f6
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 41 deletions.
131 changes: 94 additions & 37 deletions BaseTools/Plugin/OverrideValidation/OverrideValidation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# This tool depends on EDK2 and will parse dsc files, inf files and other standard
# EDK2 assets
#
# This tool also generates deprecation warnings if a given file is deprecated
#
# Copyright (c) Microsoft Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
Expand Down Expand Up @@ -32,20 +34,21 @@
from edk2toollib.utility_functions import RunCmd

#Tuple for (version, entrycount)
FORMAT_VERSION_1 = (1, 4) #Version 1: #OVERRIDE : VERSION | PATH_TO_MODULE | HASH | YYYY-MM-DDThh-mm-ss
FORMAT_VERSION_2 = (2, 5) #Version 2: #OVERRIDE : VERSION | PATH_TO_MODULE | HASH | YYYY-MM-DDThh-mm-ss | GIT_COMMIT
FORMAT_VERSIONS = [FORMAT_VERSION_1, FORMAT_VERSION_2]

OVERRIDE_FORMAT_VERSION_1 = (1, 4) #Version 1: #OVERRIDE : VERSION | PATH_TO_MODULE | HASH | YYYY-MM-DDThh-mm-ss
OVERRIDE_FORMAT_VERSION_2 = (2, 5) #Version 2: #OVERRIDE : VERSION | PATH_TO_MODULE | HASH | YYYY-MM-DDThh-mm-ss | GIT_COMMIT
DEPRECATION_FORMAT_VERSION_1 = (1, 3) # Version 1: # DEPRECATED : VERSION | PATH_TO_NEW_MODULE_TO_USE | YYYY-MM-DDThh-mm-ss
FORMAT_VERSIONS = [OVERRIDE_FORMAT_VERSION_1, OVERRIDE_FORMAT_VERSION_2, DEPRECATION_FORMAT_VERSION_1]

class OverrideValidation(IUefiBuildPlugin):

class OverrideResult(object):
OR_ALL_GOOD = 0
OR_FILE_CHANGE = 1
OR_VER_UNRECOG = 2
OR_INVALID_FORMAT = 3
OR_DSC_INF_NOT_FOUND = 4
OR_TARGET_INF_NOT_FOUND = 5
OR_ALL_GOOD = 0
OR_FILE_CHANGE = 1
OR_VER_UNRECOG = 2
OR_INVALID_FORMAT = 3
OR_DSC_INF_NOT_FOUND = 4
OR_TARGET_INF_NOT_FOUND = 5
OR_DEPRECATED_MODULE_USED = 6

@classmethod
def GetErrStr (cls, errcode):
Expand All @@ -62,6 +65,8 @@ def GetErrStr (cls, errcode):
str = 'FILE_NOT_FOUND'
elif (errcode == cls.OR_TARGET_INF_NOT_FOUND):
str = 'INF_FILE_NOT_FOUND'
elif (errcode == cls.OR_DEPRECATED_MODULE_USED):
str = 'DEPRECATED_MODULE_USED'
else:
str = 'UNKNOWN'
return str
Expand Down Expand Up @@ -176,20 +181,21 @@ def override_detect_process(self, thebuilder, filepath, filelist, modulenode, st
CommentLine = Line.strip('#').split(':')
if (len(CommentLine) != 2) or\
((CommentLine[0].strip().lower() != 'override') and\
(CommentLine[0].strip().lower() != 'track')):
(CommentLine[0].strip().lower() != 'track') and
(CommentLine[0].strip().lower() != 'deprecated')):
continue

# Process the override content, 1. Bail on bad data; 2. Print on formatted data (matched or not)
tagtype = CommentLine[0].strip().lower()
m_result = self.override_process_line(thebuilder, CommentLine[1], filepath, filelist, modulenode, status, tagtype)

if CommentLine[0].strip().lower() == 'override':
if tagtype == 'override':
# For override tags, the hash has to match
if m_result != self.OverrideResult.OR_ALL_GOOD:
result = m_result
logging.error("At Line %d: %s" %(lineno, Line))

elif CommentLine[0].strip().lower() == 'track':
elif tagtype == 'track':
# For track tags, ignore the tags of which inf modules are not found
trackno = trackno + 1
if m_result == self.OverrideResult.OR_TARGET_INF_NOT_FOUND:
Expand All @@ -204,6 +210,10 @@ def override_detect_process(self, thebuilder, filepath, filelist, modulenode, st
else:
track_ag.append(modulenode.reflist[-1].path)

elif tagtype == 'deprecated':
if m_result == self.OverrideResult.OR_DEPRECATED_MODULE_USED:
logging.warning("At Line %d: %s" %(lineno, Line))

if trackno != 0 and len(track_nf) == trackno:
# All track tags in this file are not found, this will enforce a failure, if not already failed
if result == self.OverrideResult.OR_ALL_GOOD:
Expand Down Expand Up @@ -275,7 +285,9 @@ def override_process_line(self, thebuilder, overridecnt, filepath, filelist, mod
m_node.status = result
return result

if version_match[0] == 1:
if version_match[0] == 1 and tagtype == 'deprecated':
return self.deprecation_process_line_with_version1(thebuilder, filepath, OverrideEntry)
elif version_match[0] == 1:
return self.override_process_line_with_version1(thebuilder, filelist, OverrideEntry, m_node, status, tagtype)
elif version_match[0] == 2:
return self.override_process_line_with_version2(thebuilder, filelist, OverrideEntry, m_node, status, tagtype)
Expand Down Expand Up @@ -366,6 +378,12 @@ def override_process_line_with_version2(self, thebuilder, filelist, OverrideEntr
return result
# END: override_process_line_version2(self, thebuilder, filelist, OverrideEntry, m_node, status)

def deprecation_process_line_with_version1(self, thebuilder, filepath, DeprecationEntry):
if thebuilder.env.GetValue("ALLOW_DEPRECATED_MODULES") == "TRUE":
logging.info("Deprecated modules check is disabled")
return self.OverrideResult.OR_ALL_GOOD
logging.warning("Use of Deprecated module: %s, Please switch to: %s.", filepath, thebuilder.edk2path.GetAbsolutePathOnThisSystemFromEdk2RelativePath(DeprecationEntry[1].strip()))

# Check override record against parsed entries
# version: Override record's version number, normally parsed from the override record line
# hash: Override record's hash field, normally parsed from the override record line, calculated by the standalone ModuleHash tool
Expand All @@ -375,7 +393,7 @@ def override_hash_compare(self, thebuilder, version, hash, fullpath):
hash_val = ''

# Error out the unknown version
if (version == FORMAT_VERSION_1[0]):
if (version == OVERRIDE_FORMAT_VERSION_1[0]):
hash_val = ModuleHashCal(fullpath)
if (hash_val != hash):
result = self.OverrideResult.OR_FILE_CHANGE
Expand Down Expand Up @@ -582,6 +600,14 @@ def path_parse():
'-r', '--regenpath', dest = 'RegenPath', type=str,
help = '''Specify the absolute path to an inf with existing overrides to regen by passing r Path/To/Target or --regenpath Path/To/Target.'''
)
group.add_argument (
'-d', '--deprecatepath', dest = 'DeprecatedPath', type=str,
help = '''Specify the absolute path to the file to be deprecated by passing -d DEPRECATED_MODULE_PATH or --deprecatepath DEPRECATED_MODULE_PATH.'''
)
parser.add_argument (
'-dr', '--deprecationreplacementpath', dest = 'DeprecationReplacementPath', type=str,
help = '''Specify the relative path from the package to the replacement module by passing -dr Path/To/ReplacementPath or --deprecationreplacementpath Path/To/Target.'''
)
parser.add_argument (
'-p', '--packagepath', dest = 'RegenPackagePath', nargs="*", default=[],
help = '''Specify the packages path to be used to resolve relative paths when using --regenpath. ignored otherwise. Workspace is always included.'''
Expand All @@ -608,7 +634,7 @@ def path_parse():
if not os.path.isfile(Paths.TargetPath):
raise RuntimeError("Module path is invalid.")

if Paths.Version < 1 or Paths.Version > len(FORMAT_VERSIONS):
if Paths.Version not in [version_format[0] for version_format in FORMAT_VERSIONS]:
raise RuntimeError("Version is invalid")

if not os.path.isdir(Paths.WorkSpace):
Expand All @@ -631,6 +657,15 @@ def path_parse():
if not os.path.normcase(Paths.RegenPath).startswith(os.path.normcase(Paths.WorkSpace.rstrip(os.sep)) + os.sep):
raise RuntimeError("Module is not within specified Workspace.")

if Paths.DeprecatedPath is not None:
if not os.path.isfile(Paths.DeprecatedPath):
raise RuntimeError("Deprecation path module is invalid")
# Needs to strip os.sep is to take care of the root path case
# For a folder, this will do nothing on a formatted abspath
# For a drive root, this will rip off the os.sep
if not os.path.normcase(Paths.DeprecatedPath).startswith(os.path.normcase(Paths.WorkSpace.rstrip(os.sep)) + os.sep):
raise RuntimeError("Module is not within specified Workspace.")

return Paths

################################################
Expand Down Expand Up @@ -671,10 +706,10 @@ def path_parse():
VERSION_INDEX = Paths.Version - 1

if VERSION_INDEX == 0:
line = '#%s : %08d | %s | %s | %s\n' % (match.group(1), FORMAT_VERSION_1[0], rel_path, mod_hash, datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S"))
line = '#%s : %08d | %s | %s | %s\n' % (match.group(1), OVERRIDE_FORMAT_VERSION_1[0], rel_path, mod_hash, datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S"))
elif VERSION_INDEX == 1:
git_hash = ModuleGitHash(abs_path)
line = '#%s : %08d | %s | %s | %s | %s\n' % (match.group(1), FORMAT_VERSION_2[0], rel_path, mod_hash, datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S"), git_hash)
line = '#%s : %08d | %s | %s | %s | %s\n' % (match.group(1), OVERRIDE_FORMAT_VERSION_2[0], rel_path, mod_hash, datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S"), git_hash)
print("Updating:\n" + line)
else:
print(f"Warning: Could not resolve relative path {rel_path}. Override line not updated.\n")
Expand All @@ -687,28 +722,50 @@ def path_parse():
else:
dummy_list = []
pathtool = Edk2Path(Paths.WorkSpace, dummy_list)
if Paths.DeprecatedPath is not None:
# Generate deprecation warning line
# DEPRECATION_FORMAT_VERSION_1 = (1, 4) # Version 1: # DEPRECATED : VERSION | PATH_TO_NEW_MODULE_TO_USE | YYYY-MM-DDThh-mm-ss

if Paths.DeprecationReplacementPath is not None:
pkg_path = pathtool.GetContainingPackage(Paths.DeprecationReplacementPath)
if pkg_path is not None:
rel_path = Paths.DeprecationReplacementPath[Paths.DeprecationReplacementPath.find(pkg_path):]
else:
rel_path = pathtool.GetEdk2RelativePathFromAbsolutePath(Paths.DeprecationReplacementPath)
if not rel_path:
print(f"{Paths.DeprecationReplacementPath} is invalid for this workspace.")
sys.exit(1)
rel_path = rel_path.replace('\\', '/')
else:
rel_path = "REMOVED_COMPLETELY"

print("Copy and paste the following line(s) to your deprecated inf file(s):\n")
print('#%s : %08d | %s | %s \n' % ('Deprecated', DEPRECATION_FORMAT_VERSION_1[0], rel_path, datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S")))

# Generate and print the override for pasting into the file.
# Use absolute module path to find package path
pkg_path = pathtool.GetContainingPackage(Paths.TargetPath)
if pkg_path is not None:
rel_path = Paths.TargetPath[Paths.TargetPath.find(pkg_path):]
else:
rel_path = pathtool.GetEdk2RelativePathFromAbsolutePath(Paths.TargetPath)
if not rel_path:
print(f"{Paths.TargetPath} is invalid for this workspace.")
sys.exit(1)
# Generate override hash

# Generate and print the override for pasting into the file.
# Use absolute module path to find package path
pkg_path = pathtool.GetContainingPackage(Paths.TargetPath)
if pkg_path is not None:
rel_path = Paths.TargetPath[Paths.TargetPath.find(pkg_path):]
else:
rel_path = pathtool.GetEdk2RelativePathFromAbsolutePath(Paths.TargetPath)
if not rel_path:
print(f"{Paths.TargetPath} is invalid for this workspace.")
sys.exit(1)

rel_path = rel_path.replace('\\', '/')
mod_hash = ModuleHashCal(Paths.TargetPath)
rel_path = rel_path.replace('\\', '/')
mod_hash = ModuleHashCal(Paths.TargetPath)

VERSION_INDEX = Paths.Version - 1
VERSION_INDEX = Paths.Version - 1

if VERSION_INDEX == 0:
print("Copy and paste the following line(s) to your overrider inf file(s):\n")
print('#%s : %08d | %s | %s | %s' % ("Override" if not Paths.Track else "Track", FORMAT_VERSION_1[0], rel_path, mod_hash, datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S")))
if VERSION_INDEX == 0:
print("Copy and paste the following line(s) to your overrider inf file(s):\n")
print('#%s : %08d | %s | %s | %s' % ("Override" if not Paths.Track else "Track", OVERRIDE_FORMAT_VERSION_1[0], rel_path, mod_hash, datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S")))

elif VERSION_INDEX == 1:
git_hash = ModuleGitHash(Paths.TargetPath)
print("Copy and paste the following line(s) to your overrider inf file(s):\n")
print('#%s : %08d | %s | %s | %s | %s' % ("Override" if not Paths.Track else "Track", FORMAT_VERSION_2[0], rel_path, mod_hash, datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S"), git_hash))
elif VERSION_INDEX == 1:
git_hash = ModuleGitHash(Paths.TargetPath)
print("Copy and paste the following line(s) to your overrider inf file(s):\n")
print('#%s : %08d | %s | %s | %s | %s' % ("Override" if not Paths.Track else "Track", OVERRIDE_FORMAT_VERSION_2[0], rel_path, mod_hash, datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%S"), git_hash))
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"scope": "global",
"name": "Override Validation Pre Build Plugin",
"name": "Override Validation and Deprecation Warning Pre Build Plugin",
"module": "OverrideValidation"
}
35 changes: 32 additions & 3 deletions BaseTools/Plugin/OverrideValidation/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,29 @@ Override record to be included in overriding module's inf:
#Override : 00000002 | MdeModulePkg/Library/BaseSerialPortLib16550 | 140759cf30a73b02f48cc1f226b015d8 | 2021-12-07T05-30-10 | fa99a33fdb7e8bf6063513fddb807105ec2fad81
```

Command to generate a deprecation record:

``` cmd
OverrideValidation.py -w C:\Repo -d C:\Repo\MU_BASECORE\MdeModulePkg\BaseMemoryLib\BaseMemoryLib.inf -dr C:\Repo\MU_BASECORE\MdeModulePkg\BaseMemoryLibV2\BaseMemoryLib.inf
```

Deprecation record to be included in the deprecated module's inf:

``` cmd
#Deprecated : 00000001 | MdeModulePkg/BaseMemoryLibV2/BaseMemoryLib.inf | 2024-02-16T04-00-28
```

Deprecation warning example:

``` cmd
WARNING - Use of Deprecated module: C:\Repo\MU_BASECORE\MdeModulePkg\BaseMemoryLib\BaseMemoryLib.inf, Please switch to: C:\Repo\MU_BASECORE\MdeModulePkg\BaseMemoryLibV2\BaseMemoryLib.inf.
```

To disable Deprecation warnings, Set the following value in the build scripts.

``` cmd
self.env.SetValue("ALLOW_DEPRECATED_MODULES", "TRUE", "Allow the usage of deprecated modules")
```
Override log generated during pre-build process:

``` cmd
Expand Down Expand Up @@ -151,15 +174,15 @@ index 2d4ca47299..90da207a39 100644

## Versions

There are two versions of the override format.
There are two versions of the override format and one version of the deprecation format

### Version 1
### Override Version 1

``` cmd
#Override : 00000001 | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf | cc255d9de141fccbdfca9ad02e0daa47 | 2018-05-09T17-54-17
```

### Version 2
### Override Version 2

``` cmd
#Override : 00000002 | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf | cc255d9de141fccbdfca9ad02e0daa47 | 2018-05-09T17-54-17 | 575096df6a
Expand All @@ -170,6 +193,12 @@ upstream was last updated. This allows to tools to do a `git diff` between what
you currently have and what is in the tree. It currently only diffs the
overridden file (the INF or DSC) and the overriding file.

### Deprecation Version 1

``` cmd
#Deprecated : 00000001 | MdeModulePkg/BaseMemoryLibV2/BaseMemoryLib.inf | 2024-02-16T04-00-28
```

## Copyright & License

Copyright (c) Microsoft Corporation. All rights reserved.
Expand Down

0 comments on commit 76157f6

Please sign in to comment.