09d4d224 |
1 | #!/usr/bin/env python |
2 | |
3b7a53b6 |
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 | |
09d4d224 |
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") |
af2efcaf |
132 | packageVersion = XmlElement(spd, "PackageSurfaceArea/SpdHeader/Version") |
133 | packageGuid = XmlElement(spd, "PackageSurfaceArea/SpdHeader/GuidValue") |
09d4d224 |
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. |
af2efcaf |
146 | cname_table[cname_text] = {"name": packageName, "version": packageVersion, "guid": packageGuid} |
147 | |
09d4d224 |
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. |
af2efcaf |
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))) |