7742cf9cdc0194a105dbbe677e4c99ade4ebac4b
3 """Calculate the dependencies a given module has by looking through the source
4 code to see what guids and functions are referenced to see which Packages and
5 Library Classes need to be referenced. """
7 import os
, sys
, re
, getopt
, string
, glob
, xml
.dom
.minidom
, pprint
9 # Map each function name back to the lib class that declares it.
12 # Map each guid name to a package name.
15 def XmlList(Dom
, String
):
16 """Get a list of XML Elements using XPath style syntax."""
17 if Dom
.nodeType
==Dom
.DOCUMENT_NODE
:
18 return XmlList(Dom
.documentElement
, String
)
20 return XmlList(Dom
, String
[1:])
23 TagList
= String
.split('/')
25 if Dom
.nodeType
== Dom
.ELEMENT_NODE
and Dom
.tagName
.strip() == TagList
[0]:
29 restOfPath
= "/".join(TagList
[1:])
30 for child
in Dom
.childNodes
:
31 nodes
= nodes
+ XmlList(child
, restOfPath
)
34 def XmlElement (Dom
, String
):
35 """Return a single element that matches the String which is XPath style syntax."""
37 return XmlList (Dom
, String
)[0].firstChild
.data
.strip(' ')
41 def XmlElementData (Dom
):
42 """Get the text for this element."""
43 return Dom
.firstChild
.data
.strip(' ')
45 def XmlAttribute (Dom
, String
):
46 """Return a single attribute that named by String."""
48 return Dom
.getAttribute(String
)
52 def inWorkspace(rel_path
):
53 """Treat the given path as relative to the workspace."""
55 # Make sure the user has set the workspace variable:
57 return os
.path
.join(os
.environ
["WORKSPACE"], rel_path
)
59 print "Oops! You must set the WORKSPACE environment variable to run this script."
62 def getIdentifiers(infiles
):
64 """Build a set of all the identifiers in this file."""
66 # Start with an empty set.
69 for infile
in infiles
:
74 # Create some lexical categories that we will use to filter out
75 strings
=re
.compile('L?"[^"]*"')
76 chars
=re
.compile("'[^']*'")
77 hex=re
.compile("0[Xx][0-9a-fA-F]*")
78 keywords
= re
.compile('for|do|while|if|else|break|int|unsigned|switch|volatile|goto|case|char|long|struct|return|extern')
79 common
= re
.compile('VOID|UINTN|UINT32|UINT8|UINT64')
81 # Compile a Regular expression to grab all the identifers from the input.
82 identifier
= re
.compile('[_a-zA-Z][0-9_a-zA-Z]{3,}')
84 for line
in f
.readlines():
86 # Filter some lexical categories out.
87 # for filter in [strings, chars, hex, keywords, common]:
88 for filter in [strings
, chars
, hex]:
89 line
= re
.sub(filter, '', line
)
91 # Add all the identifiers that we found on this line.
92 ids
= ids
.union(set(identifier
.findall(line
)))
97 # Return the set of identifiers.
101 def search_classes(ids
):
103 """ Search the set of classes for functions."""
105 # Start with an empty set.
110 # If it is not a "hit" in the table add it to the set.
111 classes
.add(function_table
[id])
113 # If it is not a "hit" in the table, ignore it.
118 def search_cnames(ids
):
120 """Search all the Packages to see if this code uses a Guid from one of them.
121 Return a set of matching packages."""
127 # If it is not a "hit" in the table add it to the set.
128 packages
.add(cname_table
[id])
130 # If it is not a "hit" in the table, ignore it.
137 """Open the database and get all the spd files out."""
140 database
= xml
.dom
.minidom
.parse(inWorkspace("Tools/Conf/FrameworkDatabase.db"))
142 # Get a list of all the packages
143 for filename
in XmlList(database
, "/FrameworkDatabase/PackageList/Filename"):
144 spdFile
= XmlElementData(filename
)
146 # Now open the spd file and build the database of guids.
147 getCNames(inWorkspace(spdFile
))
148 getLibClasses(inWorkspace(spdFile
))
150 def getCNames(spdFile
):
152 """Extract all the C_Names from an spd file."""
154 # Begin to parse the XML of the .spd
155 spd
= xml
.dom
.minidom
.parse(spdFile
)
157 # Get the name of the package
158 packageName
= XmlElement(spd
, "PackageSurfaceArea/SpdHeader/PackageName")
161 for cname
in XmlList(spd
, "/PackageSurfaceArea/GuidDeclarations/Entry/C_Name") + \
162 XmlList(spd
, "/PackageSurfaceArea/PcdDeclarations/PcdEntry/C_Name") + \
163 XmlList(spd
, "/PackageSurfaceArea/PpiDeclarations/Entry/C_Name") + \
164 XmlList(spd
, "/PackageSurfaceArea/ProtocolDeclarations/Entry/C_Name"):
166 # Get the text of the <C_Name> tag.
167 cname_text
= XmlElementData(cname
)
169 # Map the <C_Name> to the <PackageName>. We will use this to lookup every
170 # identifier in the Input Code.
171 cname_table
[cname_text
] = packageName
175 def getLibClasses(spdFile
):
177 """Extract all the Lib Classes from an spd file."""
179 # Begin to parse the XML of the .spd
180 spd
= xml
.dom
.minidom
.parse(spdFile
)
182 # Get the guid of the package
183 packageGuid
= XmlElement(spd
, "/PackageSurfaceArea/SpdHeader/GuidValue")
185 for libClass
in XmlList(spd
, "/PackageSurfaceArea/LibraryClassDeclarations/LibraryClass"):
186 className
= XmlAttribute(libClass
, "Name")
187 headerfile
= XmlElementData(libClass
.getElementsByTagName("IncludeHeader")[0])
189 packageRoot
=os
.path
.dirname(spdFile
)
191 headerfile
= os
.path
.join(packageRoot
, headerfile
)
195 # This pattern can pick out function names if the EFI coding
196 # standard is followed. We could also use dumpbin on library
197 # instances to get a list of symbols.
198 functionPattern
= re
.compile("([_a-zA-Z][_a-zA-Z0-9]*) *\( *");
200 for line
in f
.readlines():
201 m
= functionPattern
.match(line
)
203 functionName
= m
.group(1)
205 function_table
[functionName
] = (className
, packageGuid
)
210 """Make a guid number out of a guid hex string."""
211 return long(strVal
.replace('-',''), 16)
213 # This acts like the main() function for the script, unless it is 'import'ed into another
215 if __name__
== '__main__':
217 # Create a pretty printer for dumping data structures in a readable form.
218 pp
= pprint
.PrettyPrinter(indent
=2)
220 # Process the command line args.
221 optlist
, args
= getopt
.getopt(sys
.argv
[1:], 'h', [ 'example-long-arg=', 'testing'])
223 """You should pass a file name as a paramter. It should be preprocessed text
224 of all the .c and .h files in your module, which is cat'ed together into one
227 # Scrape out all the things that look like identifiers.
228 ids
= getIdentifiers(args
)
230 # Read in the spds from the workspace to find the Guids.
234 print pp
.pprint(function_table
)
235 print pp
.pprint(cname_table
)
236 print "Classes = ", pp
.pprint(list(search_classes(ids
)))
237 print "C_Names = ", pp
.pprint(list(search_cnames(ids
)))