summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/BaseTools/Scripts/DetectNotUsedItem.py
blob: 1c77ed77469895976005f6e4b128a1f7c41b354f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
## @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()