]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/env python | |
2 | ||
3 | # Copyright (c) 2007, Intel Corporation | |
4 | # All rights reserved. This program and the accompanying materials | |
5 | # are licensed and made available under the terms and conditions of the BSD License | |
6 | # which accompanies this distribution. The full text of the license may be found at | |
7 | # http://opensource.org/licenses/bsd-license.php | |
8 | # | |
9 | # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
10 | # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
11 | ||
12 | """Calculate the dependencies a given module has by looking through the source | |
13 | code to see what guids and functions are referenced to see which Packages and | |
14 | Library Classes need to be referenced. """ | |
15 | ||
16 | import os, sys, re, getopt, string, glob, xml.dom.minidom, pprint | |
17 | from XmlRoutines import * | |
18 | ||
19 | # Map each function name back to the lib class that declares it. | |
20 | function_table = {} | |
21 | ||
22 | # Map each guid name to a package name. | |
23 | cname_table = {} | |
24 | ||
25 | def inWorkspace(rel_path): | |
26 | """Treat the given path as relative to the workspace.""" | |
27 | ||
28 | # Make sure the user has set the workspace variable: | |
29 | try: | |
30 | return os.path.join(os.environ["WORKSPACE"], rel_path ) | |
31 | except: | |
32 | print "Oops! You must set the WORKSPACE environment variable to run this script." | |
33 | sys.exit() | |
34 | ||
35 | def getIdentifiers(infiles): | |
36 | ||
37 | """Build a set of all the identifiers in this file.""" | |
38 | ||
39 | # Start with an empty set. | |
40 | ids = set() | |
41 | ||
42 | for infile in infiles: | |
43 | ||
44 | # Open the file | |
45 | f = open(infile) | |
46 | ||
47 | # Create some lexical categories that we will use to filter out | |
48 | strings=re.compile('L?"[^"]*"') | |
49 | chars=re.compile("'[^']*'") | |
50 | hex=re.compile("0[Xx][0-9a-fA-F]*") | |
51 | keywords = re.compile('for|do|while|if|else|break|int|unsigned|switch|volatile|goto|case|char|long|struct|return|extern') | |
52 | common = re.compile('VOID|UINTN|UINT32|UINT8|UINT64') | |
53 | ||
54 | # Compile a Regular expression to grab all the identifers from the input. | |
55 | identifier = re.compile('[_a-zA-Z][0-9_a-zA-Z]{3,}') | |
56 | ||
57 | for line in f.readlines(): | |
58 | ||
59 | # Filter some lexical categories out. | |
60 | # for filter in [strings, chars, hex, keywords, common]: | |
61 | for filter in [strings, chars, hex]: | |
62 | line = re.sub(filter, '', line) | |
63 | ||
64 | # Add all the identifiers that we found on this line. | |
65 | ids = ids.union(set(identifier.findall(line))) | |
66 | ||
67 | # Close the file | |
68 | f.close() | |
69 | ||
70 | # Return the set of identifiers. | |
71 | return ids | |
72 | ||
73 | ||
74 | def search_classes(ids): | |
75 | ||
76 | """ Search the set of classes for functions.""" | |
77 | ||
78 | # Start with an empty set. | |
79 | classes = set() | |
80 | ||
81 | for id in ids: | |
82 | try: | |
83 | # If it is not a "hit" in the table add it to the set. | |
84 | classes.add(function_table[id]) | |
85 | except: | |
86 | # If it is not a "hit" in the table, ignore it. | |
87 | pass | |
88 | ||
89 | return classes | |
90 | ||
91 | def search_cnames(ids): | |
92 | ||
93 | """Search all the Packages to see if this code uses a Guid from one of them. | |
94 | Return a set of matching packages.""" | |
95 | ||
96 | packages = set() | |
97 | ||
98 | for id in ids: | |
99 | try: | |
100 | # If it is not a "hit" in the table add it to the set. | |
101 | packages.add(cname_table[id]) | |
102 | except: | |
103 | # If it is not a "hit" in the table, ignore it. | |
104 | pass | |
105 | ||
106 | return packages | |
107 | ||
108 | def getSpds(): | |
109 | ||
110 | """Open the database and get all the spd files out.""" | |
111 | ||
112 | # Open the database | |
113 | database = xml.dom.minidom.parse(inWorkspace("Tools/Conf/FrameworkDatabase.db")) | |
114 | ||
115 | # Get a list of all the packages | |
116 | for filename in XmlList(database, "/FrameworkDatabase/PackageList/Filename"): | |
117 | spdFile = XmlElementData(filename) | |
118 | ||
119 | # Now open the spd file and build the database of guids. | |
120 | getCNames(inWorkspace(spdFile)) | |
121 | getLibClasses(inWorkspace(spdFile)) | |
122 | ||
123 | def getCNames(spdFile): | |
124 | ||
125 | """Extract all the C_Names from an spd file.""" | |
126 | ||
127 | # Begin to parse the XML of the .spd | |
128 | spd = xml.dom.minidom.parse(spdFile) | |
129 | ||
130 | # Get the name of the package | |
131 | packageName = XmlElement(spd, "PackageSurfaceArea/SpdHeader/PackageName") | |
132 | packageVersion = XmlElement(spd, "PackageSurfaceArea/SpdHeader/Version") | |
133 | packageGuid = XmlElement(spd, "PackageSurfaceArea/SpdHeader/GuidValue") | |
134 | ||
135 | # Find the C_Name | |
136 | for cname in XmlList(spd, "/PackageSurfaceArea/GuidDeclarations/Entry/C_Name") + \ | |
137 | XmlList(spd, "/PackageSurfaceArea/PcdDeclarations/PcdEntry/C_Name") + \ | |
138 | XmlList(spd, "/PackageSurfaceArea/PpiDeclarations/Entry/C_Name") + \ | |
139 | XmlList(spd, "/PackageSurfaceArea/ProtocolDeclarations/Entry/C_Name"): | |
140 | ||
141 | # Get the text of the <C_Name> tag. | |
142 | cname_text = XmlElementData(cname) | |
143 | ||
144 | # Map the <C_Name> to the <PackageName>. We will use this to lookup every | |
145 | # identifier in the Input Code. | |
146 | cname_table[cname_text] = {"name": packageName, "version": packageVersion, "guid": packageGuid} | |
147 | ||
148 | ||
149 | return | |
150 | ||
151 | def getLibClasses(spdFile): | |
152 | ||
153 | """Extract all the Lib Classes from an spd file.""" | |
154 | ||
155 | # Begin to parse the XML of the .spd | |
156 | spd = xml.dom.minidom.parse(spdFile) | |
157 | ||
158 | # Get the guid of the package | |
159 | packageGuid = XmlElement(spd, "/PackageSurfaceArea/SpdHeader/GuidValue") | |
160 | ||
161 | for libClass in XmlList(spd, "/PackageSurfaceArea/LibraryClassDeclarations/LibraryClass"): | |
162 | className = XmlAttribute(libClass, "Name") | |
163 | headerfile = XmlElementData(libClass.getElementsByTagName("IncludeHeader")[0]) | |
164 | ||
165 | packageRoot=os.path.dirname(spdFile) | |
166 | ||
167 | headerfile = os.path.join(packageRoot, headerfile) | |
168 | ||
169 | f = open(headerfile) | |
170 | ||
171 | # This pattern can pick out function names if the EFI coding | |
172 | # standard is followed. We could also use dumpbin on library | |
173 | # instances to get a list of symbols. | |
174 | functionPattern = re.compile("([_a-zA-Z][_a-zA-Z0-9]*) *\( *"); | |
175 | ||
176 | for line in f.readlines(): | |
177 | m = functionPattern.match(line) | |
178 | if m: | |
179 | functionName = m.group(1) | |
180 | # Map it! | |
181 | function_table[functionName] = (className, packageGuid) | |
182 | ||
183 | f.close() | |
184 | ||
185 | def guid(strVal): | |
186 | """Make a guid number out of a guid hex string.""" | |
187 | return long(strVal.replace('-',''), 16) | |
188 | ||
189 | # This acts like the main() function for the script, unless it is 'import'ed into another | |
190 | # script. | |
191 | if __name__ == '__main__': | |
192 | ||
193 | # Create a pretty printer for dumping data structures in a readable form. | |
194 | pp = pprint.PrettyPrinter(indent=2) | |
195 | ||
196 | # Process the command line args. | |
197 | optlist, args = getopt.getopt(sys.argv[1:], 'h', [ 'example-long-arg=', 'testing']) | |
198 | ||
199 | """You should pass a file name as a paramter. It should be preprocessed text | |
200 | of all the .c and .h files in your module, which is cat'ed together into one | |
201 | large file.""" | |
202 | ||
203 | # Scrape out all the things that look like identifiers. | |
204 | ids = getIdentifiers(args) | |
205 | ||
206 | # Read in the spds from the workspace to find the Guids. | |
207 | getSpds() | |
208 | ||
209 | # Debug stuff. | |
210 | print "Function Table = " | |
211 | pp.pprint(function_table) | |
212 | print "CName Table = " | |
213 | pp.pprint(cname_table) | |
214 | print "Classes = " | |
215 | pp.pprint(list(search_classes(ids))) | |
216 | print "C_Names = " | |
217 | pp.pprint(list(search_cnames(ids))) |