Skip to content

Commit

Permalink
BaseTools: Detect library class mismatch [REBASE&FF] (#499)
Browse files Browse the repository at this point in the history
## Description
Performs a check that will verify that the library instance implements
the specified library by ensuring a LIBRARY_CLASS definition exists in
the INF [Defines] section matches the library.

That is to say, for the following example:
`TestLib|Path/To/BaseTestLib.inf`, that BaseTestLib.inf has
`LIBRARY_CLASS = TestLib` defined in the [Defines] section.

Changes the previous check for library overrides to be a warning instead
of an error.

- [ ] Impacts functionality?
- **Functionality** - Does the change ultimately impact how firmware
functions?
- Examples: Add a new library, publish a new PPI, update an algorithm,
...
- [ ] Impacts security?
- **Security** - Does the change have a direct security impact on an
application,
    flow, or firmware?
  - Examples: Crypto algorithm change, buffer overflow fix, parameter
    validation improvement, ...
- [ ] Breaking change?
- **Breaking change** - Will anyone consuming this change experience a
break
    in build or boot behavior?
- Examples: Add a new library class, move a module to a different repo,
call
    a function in a new library class in a pre-existing module, ...
- [ ] Includes tests?
  - **Tests** - Does the change include any explicit test code?
  - Examples: Unit tests, integration tests, robot tests, ...
- [ ] Includes documentation?
- **Documentation** - Does the change contain explicit documentation
additions
    outside direct code modifications (and comments)?
- Examples: Update readme file, add feature readme file, link to
documentation
    on an a separate Web page, ...

## How This Was Tested

Built multiple platforms to confirm warning showed properly.

## Integration Instructions

While no Integration is required, a new build warning will be seen in
the build log. The warning is in the following format:
`$(DSC_PATH): warning: $(INF) does not support LIBRARY_CLASS
$(LIBRARY_CLASS)` where `$(DSC_PATH)` is the DSC being built, `$(INF)`
is the INF described in the DSC, and `$(LIBRARY_CLASS)` is the name of
the library class that the INF is attempting to represent. (i.e.,
`$(LIBRARY_CLASS)|$(INF)` or `TestLib|Path\to\BaseTestLib.inf`).

To resolve these errors, verify the library class is real (The Library
class should have a header file associated with it in the DEC of the
defining package). If it is real, then ensure the library instance truly
implements the library class and add it to the DEFINE section of the
library INF. If not, update the DSC to use the correct library class.

If this warning appears, it will appear at the beginning of the build
command:
INFO - Processing meta-data .
INFO - Architecture(s)  = IA32 X64
INFO - Build target     = DEBUG
INFO - Toolchain        = VS2022
INFO - 
INFO - Active Platform          = c:\src\Path\Platform.dsc
INFO - build...
INFO - c:\src\Path\Platform.dsc(...): warning:
c:\src\Path\BaseTestLib.inf does not support LIBRARY_CLASS TestLib
  • Loading branch information
Javagedes authored and kenlautner committed Oct 17, 2023
1 parent ff81275 commit 89a7b3a
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 21 deletions.
1 change: 1 addition & 0 deletions BaseTools/Source/Python/AutoGen/AutoGenWorker.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ def run(self):
GlobalData.gEnableGenfdsMultiThread = self.data_pipe.Get("EnableGenfdsMultiThread")
GlobalData.gPlatformFinalPcds = self.data_pipe.Get("gPlatformFinalPcds")
GlobalData.file_lock = self.file_lock
GlobalData.gLogLibraryMismatch = False # MU_CHANGE
CommandTarget = self.data_pipe.Get("CommandTarget")
pcd_from_build_option = []
for pcd_tuple in self.data_pipe.Get("BuildOptPcd"):
Expand Down
2 changes: 1 addition & 1 deletion BaseTools/Source/Python/Common/GlobalData.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,4 @@
gSikpAutoGenCache = set()
# Common lock for the file access in multiple process AutoGens
file_lock = None
gLogLibraryMismatch = True # MU_CHANGE
42 changes: 42 additions & 0 deletions BaseTools/Source/Python/Workspace/DscBuildData.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
from Workspace.BuildClassObject import PlatformBuildClassObject, StructurePcd, PcdClassObject, ModuleBuildClassObject
from collections import OrderedDict, defaultdict

LoggedLibraryWarnings = [] # MU_CHANGE

def _IsFieldValueAnArray (Value):
Value = Value.strip()
if Value.startswith(TAB_GUID) and Value.endswith(')'):
Expand Down Expand Up @@ -763,6 +765,14 @@ def Modules(self):
LibraryPath = PathClass(NormPath(Record[1], Macros), GlobalData.gWorkspace, Arch=self._Arch)
LineNo = Record[-1]

# MU_CHANGE begin
# Validate that the Library instance implements the Library Class
if not self._ValidateLibraryClass(LibraryClass, LibraryPath) and self._ShouldLogLibrary(LineNo):
EdkLogger.warn("build",
f"{str(LibraryPath)} does not support LIBRARY_CLASS {LibraryClass}",
File=self.MetaFile)
# MU_CHANGE end

# check the file validation
ErrorCode, ErrorInfo = LibraryPath.Validate('.inf')
if ErrorCode != 0:
Expand Down Expand Up @@ -916,6 +926,15 @@ def LibraryClasses(self):
EdkLogger.verbose("Found forced library for arch=%s\n\t%s [%s]" % (Arch, LibraryInstance, LibraryClass))
LibraryClassSet.add(LibraryClass)
LibraryInstance = PathClass(NormPath(LibraryInstance, Macros), GlobalData.gWorkspace, Arch=self._Arch)

# MU_CHANGE begin
# Validate that the Library instance implements the Library Class
if not self._ValidateLibraryClass(LibraryClass, LibraryInstance) and self._ShouldLogLibrary(LineNo):
EdkLogger.warn("build",
f"{str(LibraryInstance)} does not support LIBRARY_CLASS {LibraryClass}",
File=self.MetaFile)
# MU_CHANGE end

# check the file validation
ErrorCode, ErrorInfo = LibraryInstance.Validate('.inf')
if ErrorCode != 0:
Expand Down Expand Up @@ -1190,6 +1209,29 @@ def __ParsePcdFromCommandLine(self):
for item in delete_assign:
GlobalData.BuildOptionPcd.remove(item)

# MU_CHANGE begin
def _ValidateLibraryClass(self, LibraryClass: str, LibraryInstance: PathClass) -> bool:
if LibraryClass.upper().startswith('NULL'):
return True

ParsedLibraryInfo = self._Bdb[LibraryInstance, self._Arch, self._Target, self._Toolchain]

for LibraryClassObject in ParsedLibraryInfo.LibraryClass:
if LibraryClassObject.LibraryClass == LibraryClass:
return True
return False

def _ShouldLogLibrary(self, LineNo) -> bool:
if not GlobalData.gLogLibraryMismatch:
return False

if LineNo in LoggedLibraryWarnings:
return False

LoggedLibraryWarnings.append(LineNo)
return True
# MU_CHANGE end

@staticmethod
def HandleFlexiblePcd(TokenSpaceGuidCName, TokenCName, PcdValue, PcdDatumType, GuidDict, FieldName=''):
if FieldName:
Expand Down
20 changes: 0 additions & 20 deletions BaseTools/Source/Python/Workspace/WorkspaceCommon.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,26 +110,6 @@ def GetModuleLibInstances(Module, Platform, BuildDatabase, Arch, Target, Toolcha
if LibraryClass.startswith("NULL"):
Module.LibraryClasses[LibraryClass] = Platform.Modules[str(Module)].LibraryClasses[LibraryClass]

# MU_CHANGE begin

# Compares the Library class being over written (var: LibraryClass) to the actual library class that is
# is doing the overridding.
#
# i.e. ExampleLib|Path/To/ExampleLibBase.inf:
# ensuring ExampleLib == LIBRARY_CLASS in the define section of ExampleLibBase.inf
else:
path = Platform.Modules[str(Module)].LibraryClasses[LibraryClass]
match = False
for LibraryClassObj in BuildDatabase[path, Arch, Target, Toolchain].LibraryClass:
if LibraryClass == LibraryClassObj.LibraryClass:
match = True

if not match:
EdkLogger.error("build", BUILD_ERROR,
"LIBRARY_CLASS for override: [%s] does not match the library class being overridden: [%s]" % (path, LibraryClass),
File=FileName)
# MU_CHANGE end

# EdkII module
LibraryConsumerList = [Module]
Constructor = []
Expand Down

0 comments on commit 89a7b3a

Please sign in to comment.