Import BaseMemoryLibMmx;
[mirror_edk2.git] / Tools / Python / Calc-Deps.py
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)))