## @file | |
# Detect unreferenced PCD and GUID/Protocols/PPIs. | |
# | |
# Copyright (c) 2019, Intel Corporation. All rights reserved. | |
# | |
# SPDX-License-Identifier: BSD-2-Clause-Patent | |
# | |
''' | |
DetectNotUsedItem | |
''' | |
import re | |
import os | |
import sys | |
import argparse | |
# | |
# Globals for help information | |
# | |
__prog__ = 'DetectNotUsedItem' | |
__version__ = '%s Version %s' % (__prog__, '0.1') | |
__copyright__ = 'Copyright (c) 2019, Intel Corporation. All rights reserved.' | |
__description__ = "Detect unreferenced PCD and GUID/Protocols/PPIs.\n" | |
SectionList = ["LibraryClasses", "Guids", "Ppis", "Protocols", "Pcd"] | |
class PROCESS(object): | |
def __init__(self, DecPath, InfDirs): | |
self.Dec = DecPath | |
self.InfPath = InfDirs | |
self.Log = [] | |
def ParserDscFdfInfFile(self): | |
AllContentList = [] | |
for File in self.SearchbyExt([".dsc", ".fdf", ".inf"]): | |
AllContentList += self.ParseDscFdfInfContent(File) | |
return AllContentList | |
# Search File by extension name | |
def SearchbyExt(self, ExtList): | |
FileList = [] | |
for path in self.InfPath: | |
if type(ExtList) == type(''): | |
for root, _, files in os.walk(path, topdown=True, followlinks=False): | |
for filename in files: | |
if filename.endswith(ExtList): | |
FileList.append(os.path.join(root, filename)) | |
elif type(ExtList) == type([]): | |
for root, _, files in os.walk(path, topdown=True, followlinks=False): | |
for filename in files: | |
for Ext in ExtList: | |
if filename.endswith(Ext): | |
FileList.append(os.path.join(root, filename)) | |
return FileList | |
# Parse DEC file to get Line number and Name | |
# return section name, the Item Name and comments line number | |
def ParseDecContent(self): | |
SectionRE = re.compile(r'\[(.*)\]') | |
Flag = False | |
Comments = {} | |
Comment_Line = [] | |
ItemName = {} | |
with open(self.Dec, 'r') as F: | |
for Index, content in enumerate(F): | |
NotComment = not content.strip().startswith("#") | |
Section = SectionRE.findall(content) | |
if Section and NotComment: | |
Flag = self.IsNeedParseSection(Section[0]) | |
if Flag: | |
Comment_Line.append(Index) | |
if NotComment: | |
if content != "\n" and content != "\r\n": | |
ItemName[Index] = content.split('=')[0].split('|')[0].split('#')[0].strip() | |
Comments[Index] = Comment_Line | |
Comment_Line = [] | |
return ItemName, Comments | |
def IsNeedParseSection(self, SectionName): | |
for item in SectionList: | |
if item in SectionName: | |
return True | |
return False | |
# Parse DSC, FDF, INF File, remove comments, return Lines list | |
def ParseDscFdfInfContent(self, File): | |
with open(File, 'r') as F: | |
lines = F.readlines() | |
for Index in range(len(lines) - 1, -1, -1): | |
if lines[Index].strip().startswith("#") or lines[Index] == "\n" or lines[Index] == "\r\n": | |
lines.remove(lines[Index]) | |
elif "#" in lines[Index]: | |
lines[Index] = lines[Index].split("#")[0].strip() | |
else: | |
lines[Index] = lines[Index].strip() | |
return lines | |
def DetectNotUsedItem(self): | |
NotUsedItem = {} | |
DecItem, DecComments = self.ParseDecContent() | |
InfDscFdfContent = self.ParserDscFdfInfFile() | |
for LineNum in list(DecItem.keys()): | |
DecItemName = DecItem[LineNum] | |
Match_reg = re.compile("(?<![a-zA-Z0-9_-])%s(?![a-zA-Z0-9_-])" % DecItemName) | |
MatchFlag = False | |
for Line in InfDscFdfContent: | |
if Match_reg.search(Line): | |
MatchFlag = True | |
break | |
if not MatchFlag: | |
NotUsedItem[LineNum] = DecItemName | |
self.Display(NotUsedItem) | |
return NotUsedItem, DecComments | |
def Display(self, UnuseDict): | |
print("DEC File:\n%s\n%s%s" % (self.Dec, "{:<15}".format("Line Number"), "{:<0}".format("Unused Item"))) | |
self.Log.append( | |
"DEC File:\n%s\n%s%s\n" % (self.Dec, "{:<15}".format("Line Number"), "{:<0}".format("Unused Item"))) | |
for num in list(sorted(UnuseDict.keys())): | |
ItemName = UnuseDict[num] | |
print("%s%s%s" % (" " * 3, "{:<12}".format(num + 1), "{:<1}".format(ItemName))) | |
self.Log.append(("%s%s%s\n" % (" " * 3, "{:<12}".format(num + 1), "{:<1}".format(ItemName)))) | |
def Clean(self, UnUseDict, Comments): | |
removednum = [] | |
for num in list(UnUseDict.keys()): | |
if num in list(Comments.keys()): | |
removednum += Comments[num] | |
with open(self.Dec, 'r') as Dec: | |
lines = Dec.readlines() | |
try: | |
with open(self.Dec, 'w+') as T: | |
for linenum in range(len(lines)): | |
if linenum in removednum: | |
continue | |
else: | |
T.write(lines[linenum]) | |
print("DEC File has been clean: %s" % (self.Dec)) | |
except Exception as err: | |
print(err) | |
class Main(object): | |
def mainprocess(self, Dec, Dirs, Isclean, LogPath): | |
for dir in Dirs: | |
if not os.path.exists(dir): | |
print("Error: Invalid path for '--dirs': %s" % dir) | |
sys.exit(1) | |
Pa = PROCESS(Dec, Dirs) | |
unuse, comment = Pa.DetectNotUsedItem() | |
if Isclean: | |
Pa.Clean(unuse, comment) | |
self.Logging(Pa.Log, LogPath) | |
def Logging(self, content, LogPath): | |
if LogPath: | |
try: | |
if os.path.isdir(LogPath): | |
FilePath = os.path.dirname(LogPath) | |
if not os.path.exists(FilePath): | |
os.makedirs(FilePath) | |
with open(LogPath, 'w+') as log: | |
for line in content: | |
log.write(line) | |
print("Log save to file: %s" % LogPath) | |
except Exception as e: | |
print("Save log Error: %s" % e) | |
def main(): | |
parser = argparse.ArgumentParser(prog=__prog__, | |
description=__description__ + __copyright__, | |
conflict_handler='resolve') | |
parser.add_argument('-i', '--input', metavar="", dest='InputDec', help="Input DEC file name.") | |
parser.add_argument('--dirs', metavar="", action='append', dest='Dirs', | |
help="The package directory. To specify more directories, please repeat this option.") | |
parser.add_argument('--clean', action='store_true', default=False, dest='Clean', | |
help="Clean the unreferenced items from DEC file.") | |
parser.add_argument('--log', metavar="", dest="Logfile", default=False, | |
help="Put log in specified file as well as on console.") | |
options = parser.parse_args() | |
if options.InputDec: | |
if not (os.path.exists(options.InputDec) and options.InputDec.endswith(".dec")): | |
print("Error: Invalid DEC file input: %s" % options.InputDec) | |
if options.Dirs: | |
M = Main() | |
M.mainprocess(options.InputDec, options.Dirs, options.Clean, options.Logfile) | |
else: | |
print("Error: the following argument is required:'--dirs'.") | |
else: | |
print("Error: the following argument is required:'-i/--input'.") | |
if __name__ == '__main__': | |
main() |