1 | ## @file
|
---|
2 | # Detect unreferenced PCD and GUID/Protocols/PPIs.
|
---|
3 | #
|
---|
4 | # Copyright (c) 2019, Intel Corporation. All rights reserved.
|
---|
5 | #
|
---|
6 | # SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
7 | #
|
---|
8 |
|
---|
9 | '''
|
---|
10 | DetectNotUsedItem
|
---|
11 | '''
|
---|
12 | import re
|
---|
13 | import os
|
---|
14 | import sys
|
---|
15 | import argparse
|
---|
16 |
|
---|
17 | #
|
---|
18 | # Globals for help information
|
---|
19 | #
|
---|
20 | __prog__ = 'DetectNotUsedItem'
|
---|
21 | __version__ = '%s Version %s' % (__prog__, '0.1')
|
---|
22 | __copyright__ = 'Copyright (c) 2019, Intel Corporation. All rights reserved.'
|
---|
23 | __description__ = "Detect unreferenced PCD and GUID/Protocols/PPIs.\n"
|
---|
24 |
|
---|
25 | SectionList = ["LibraryClasses", "Guids", "Ppis", "Protocols", "Pcd"]
|
---|
26 |
|
---|
27 |
|
---|
28 | class PROCESS(object):
|
---|
29 |
|
---|
30 | def __init__(self, DecPath, InfDirs):
|
---|
31 | self.Dec = DecPath
|
---|
32 | self.InfPath = InfDirs
|
---|
33 | self.Log = []
|
---|
34 |
|
---|
35 | def ParserDscFdfInfFile(self):
|
---|
36 | AllContentList = []
|
---|
37 | for File in self.SearchbyExt([".dsc", ".fdf", ".inf"]):
|
---|
38 | AllContentList += self.ParseDscFdfInfContent(File)
|
---|
39 | return AllContentList
|
---|
40 |
|
---|
41 | # Search File by extension name
|
---|
42 | def SearchbyExt(self, ExtList):
|
---|
43 | FileList = []
|
---|
44 | for path in self.InfPath:
|
---|
45 | if type(ExtList) == type(''):
|
---|
46 | for root, _, files in os.walk(path, topdown=True, followlinks=False):
|
---|
47 | for filename in files:
|
---|
48 | if filename.endswith(ExtList):
|
---|
49 | FileList.append(os.path.join(root, filename))
|
---|
50 | elif type(ExtList) == type([]):
|
---|
51 | for root, _, files in os.walk(path, topdown=True, followlinks=False):
|
---|
52 | for filename in files:
|
---|
53 | for Ext in ExtList:
|
---|
54 | if filename.endswith(Ext):
|
---|
55 | FileList.append(os.path.join(root, filename))
|
---|
56 | return FileList
|
---|
57 |
|
---|
58 | # Parse DEC file to get Line number and Name
|
---|
59 | # return section name, the Item Name and comments line number
|
---|
60 | def ParseDecContent(self):
|
---|
61 | SectionRE = re.compile(r'\[(.*)\]')
|
---|
62 | Flag = False
|
---|
63 | Comments = {}
|
---|
64 | Comment_Line = []
|
---|
65 | ItemName = {}
|
---|
66 | with open(self.Dec, 'r') as F:
|
---|
67 | for Index, content in enumerate(F):
|
---|
68 | NotComment = not content.strip().startswith("#")
|
---|
69 | Section = SectionRE.findall(content)
|
---|
70 | if Section and NotComment:
|
---|
71 | Flag = self.IsNeedParseSection(Section[0])
|
---|
72 | if Flag:
|
---|
73 | Comment_Line.append(Index)
|
---|
74 | if NotComment:
|
---|
75 | if content != "\n" and content != "\r\n":
|
---|
76 | ItemName[Index] = content.split('=')[0].split('|')[0].split('#')[0].strip()
|
---|
77 | Comments[Index] = Comment_Line
|
---|
78 | Comment_Line = []
|
---|
79 | return ItemName, Comments
|
---|
80 |
|
---|
81 | def IsNeedParseSection(self, SectionName):
|
---|
82 | for item in SectionList:
|
---|
83 | if item in SectionName:
|
---|
84 | return True
|
---|
85 | return False
|
---|
86 |
|
---|
87 | # Parse DSC, FDF, INF File, remove comments, return Lines list
|
---|
88 | def ParseDscFdfInfContent(self, File):
|
---|
89 | with open(File, 'r') as F:
|
---|
90 | lines = F.readlines()
|
---|
91 | for Index in range(len(lines) - 1, -1, -1):
|
---|
92 | if lines[Index].strip().startswith("#") or lines[Index] == "\n" or lines[Index] == "\r\n":
|
---|
93 | lines.remove(lines[Index])
|
---|
94 | elif "#" in lines[Index]:
|
---|
95 | lines[Index] = lines[Index].split("#")[0].strip()
|
---|
96 | else:
|
---|
97 | lines[Index] = lines[Index].strip()
|
---|
98 | return lines
|
---|
99 |
|
---|
100 | def DetectNotUsedItem(self):
|
---|
101 | NotUsedItem = {}
|
---|
102 | DecItem, DecComments = self.ParseDecContent()
|
---|
103 | InfDscFdfContent = self.ParserDscFdfInfFile()
|
---|
104 | for LineNum in list(DecItem.keys()):
|
---|
105 | DecItemName = DecItem[LineNum]
|
---|
106 | Match_reg = re.compile("(?<![a-zA-Z0-9_-])%s(?![a-zA-Z0-9_-])" % DecItemName)
|
---|
107 | MatchFlag = False
|
---|
108 | for Line in InfDscFdfContent:
|
---|
109 | if Match_reg.search(Line):
|
---|
110 | MatchFlag = True
|
---|
111 | break
|
---|
112 | if not MatchFlag:
|
---|
113 | NotUsedItem[LineNum] = DecItemName
|
---|
114 | self.Display(NotUsedItem)
|
---|
115 | return NotUsedItem, DecComments
|
---|
116 |
|
---|
117 | def Display(self, UnuseDict):
|
---|
118 | print("DEC File:\n%s\n%s%s" % (self.Dec, "{:<15}".format("Line Number"), "{:<0}".format("Unused Item")))
|
---|
119 | self.Log.append(
|
---|
120 | "DEC File:\n%s\n%s%s\n" % (self.Dec, "{:<15}".format("Line Number"), "{:<0}".format("Unused Item")))
|
---|
121 | for num in list(sorted(UnuseDict.keys())):
|
---|
122 | ItemName = UnuseDict[num]
|
---|
123 | print("%s%s%s" % (" " * 3, "{:<12}".format(num + 1), "{:<1}".format(ItemName)))
|
---|
124 | self.Log.append(("%s%s%s\n" % (" " * 3, "{:<12}".format(num + 1), "{:<1}".format(ItemName))))
|
---|
125 |
|
---|
126 | def Clean(self, UnUseDict, Comments):
|
---|
127 | removednum = []
|
---|
128 | for num in list(UnUseDict.keys()):
|
---|
129 | if num in list(Comments.keys()):
|
---|
130 | removednum += Comments[num]
|
---|
131 | with open(self.Dec, 'r') as Dec:
|
---|
132 | lines = Dec.readlines()
|
---|
133 | try:
|
---|
134 | with open(self.Dec, 'w+') as T:
|
---|
135 | for linenum in range(len(lines)):
|
---|
136 | if linenum in removednum:
|
---|
137 | continue
|
---|
138 | else:
|
---|
139 | T.write(lines[linenum])
|
---|
140 | print("DEC File has been clean: %s" % (self.Dec))
|
---|
141 | except Exception as err:
|
---|
142 | print(err)
|
---|
143 |
|
---|
144 |
|
---|
145 | class Main(object):
|
---|
146 |
|
---|
147 | def mainprocess(self, Dec, Dirs, Isclean, LogPath):
|
---|
148 | for dir in Dirs:
|
---|
149 | if not os.path.exists(dir):
|
---|
150 | print("Error: Invalid path for '--dirs': %s" % dir)
|
---|
151 | sys.exit(1)
|
---|
152 | Pa = PROCESS(Dec, Dirs)
|
---|
153 | unuse, comment = Pa.DetectNotUsedItem()
|
---|
154 | if Isclean:
|
---|
155 | Pa.Clean(unuse, comment)
|
---|
156 | self.Logging(Pa.Log, LogPath)
|
---|
157 |
|
---|
158 | def Logging(self, content, LogPath):
|
---|
159 | if LogPath:
|
---|
160 | try:
|
---|
161 | if os.path.isdir(LogPath):
|
---|
162 | FilePath = os.path.dirname(LogPath)
|
---|
163 | if not os.path.exists(FilePath):
|
---|
164 | os.makedirs(FilePath)
|
---|
165 | with open(LogPath, 'w+') as log:
|
---|
166 | for line in content:
|
---|
167 | log.write(line)
|
---|
168 | print("Log save to file: %s" % LogPath)
|
---|
169 | except Exception as e:
|
---|
170 | print("Save log Error: %s" % e)
|
---|
171 |
|
---|
172 |
|
---|
173 | def main():
|
---|
174 | parser = argparse.ArgumentParser(prog=__prog__,
|
---|
175 | description=__description__ + __copyright__,
|
---|
176 | conflict_handler='resolve')
|
---|
177 | parser.add_argument('-i', '--input', metavar="", dest='InputDec', help="Input DEC file name.")
|
---|
178 | parser.add_argument('--dirs', metavar="", action='append', dest='Dirs',
|
---|
179 | help="The package directory. To specify more directories, please repeat this option.")
|
---|
180 | parser.add_argument('--clean', action='store_true', default=False, dest='Clean',
|
---|
181 | help="Clean the unreferenced items from DEC file.")
|
---|
182 | parser.add_argument('--log', metavar="", dest="Logfile", default=False,
|
---|
183 | help="Put log in specified file as well as on console.")
|
---|
184 | options = parser.parse_args()
|
---|
185 | if options.InputDec:
|
---|
186 | if not (os.path.exists(options.InputDec) and options.InputDec.endswith(".dec")):
|
---|
187 | print("Error: Invalid DEC file input: %s" % options.InputDec)
|
---|
188 | if options.Dirs:
|
---|
189 | M = Main()
|
---|
190 | M.mainprocess(options.InputDec, options.Dirs, options.Clean, options.Logfile)
|
---|
191 | else:
|
---|
192 | print("Error: the following argument is required:'--dirs'.")
|
---|
193 | else:
|
---|
194 | print("Error: the following argument is required:'-i/--input'.")
|
---|
195 |
|
---|
196 |
|
---|
197 | if __name__ == '__main__':
|
---|
198 | main()
|
---|