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