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