summaryrefslogtreecommitdiff
path: root/Tools/Python
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/Python')
-rwxr-xr-xTools/Python/Calc-Deps.py201
1 files changed, 201 insertions, 0 deletions
diff --git a/Tools/Python/Calc-Deps.py b/Tools/Python/Calc-Deps.py
new file mode 100755
index 0000000..a0afa3f
--- /dev/null
+++ b/Tools/Python/Calc-Deps.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+
+"""Calculate the dependencies a given module has by looking through the source
+code to see what guids and functions are referenced to see which Packages and
+Library Classes need to be referenced. """
+
+import os, sys, re, getopt, string, glob, xml.dom.minidom, pprint
+from XmlRoutines import *
+
+# Map each function name back to the lib class that declares it.
+function_table = {}
+
+# Map each guid name to a package name.
+cname_table = {}
+
+def inWorkspace(rel_path):
+ """Treat the given path as relative to the workspace."""
+
+ # Make sure the user has set the workspace variable:
+ try:
+ return os.path.join(os.environ["WORKSPACE"], rel_path )
+ except:
+ print "Oops! You must set the WORKSPACE environment variable to run this script."
+ sys.exit()
+
+def getIdentifiers(infiles):
+
+ """Build a set of all the identifiers in this file."""
+
+ # Start with an empty set.
+ ids = set()
+
+ for infile in infiles:
+
+ # Open the file
+ f = open(infile)
+
+ # Create some lexical categories that we will use to filter out
+ strings=re.compile('L?"[^"]*"')
+ chars=re.compile("'[^']*'")
+ hex=re.compile("0[Xx][0-9a-fA-F]*")
+ keywords = re.compile('for|do|while|if|else|break|int|unsigned|switch|volatile|goto|case|char|long|struct|return|extern')
+ common = re.compile('VOID|UINTN|UINT32|UINT8|UINT64')
+
+ # Compile a Regular expression to grab all the identifers from the input.
+ identifier = re.compile('[_a-zA-Z][0-9_a-zA-Z]{3,}')
+
+ for line in f.readlines():
+
+ # Filter some lexical categories out.
+ # for filter in [strings, chars, hex, keywords, common]:
+ for filter in [strings, chars, hex]:
+ line = re.sub(filter, '', line)
+
+ # Add all the identifiers that we found on this line.
+ ids = ids.union(set(identifier.findall(line)))
+
+ # Close the file
+ f.close()
+
+ # Return the set of identifiers.
+ return ids
+
+
+def search_classes(ids):
+
+ """ Search the set of classes for functions."""
+
+ # Start with an empty set.
+ classes = set()
+
+ for id in ids:
+ try:
+ # If it is not a "hit" in the table add it to the set.
+ classes.add(function_table[id])
+ except:
+ # If it is not a "hit" in the table, ignore it.
+ pass
+
+ return classes
+
+def search_cnames(ids):
+
+ """Search all the Packages to see if this code uses a Guid from one of them.
+ Return a set of matching packages."""
+
+ packages = set()
+
+ for id in ids:
+ try:
+ # If it is not a "hit" in the table add it to the set.
+ packages.add(cname_table[id])
+ except:
+ # If it is not a "hit" in the table, ignore it.
+ pass
+
+ return packages
+
+def getSpds():
+
+ """Open the database and get all the spd files out."""
+
+ # Open the database
+ database = xml.dom.minidom.parse(inWorkspace("Tools/Conf/FrameworkDatabase.db"))
+
+ # Get a list of all the packages
+ for filename in XmlList(database, "/FrameworkDatabase/PackageList/Filename"):
+ spdFile = XmlElementData(filename)
+
+ # Now open the spd file and build the database of guids.
+ getCNames(inWorkspace(spdFile))
+ getLibClasses(inWorkspace(spdFile))
+
+def getCNames(spdFile):
+
+ """Extract all the C_Names from an spd file."""
+
+ # Begin to parse the XML of the .spd
+ spd = xml.dom.minidom.parse(spdFile)
+
+ # Get the name of the package
+ packageName = XmlElement(spd, "PackageSurfaceArea/SpdHeader/PackageName")
+
+ # Find the C_Name
+ for cname in XmlList(spd, "/PackageSurfaceArea/GuidDeclarations/Entry/C_Name") + \
+ XmlList(spd, "/PackageSurfaceArea/PcdDeclarations/PcdEntry/C_Name") + \
+ XmlList(spd, "/PackageSurfaceArea/PpiDeclarations/Entry/C_Name") + \
+ XmlList(spd, "/PackageSurfaceArea/ProtocolDeclarations/Entry/C_Name"):
+
+ # Get the text of the <C_Name> tag.
+ cname_text = XmlElementData(cname)
+
+ # Map the <C_Name> to the <PackageName>. We will use this to lookup every
+ # identifier in the Input Code.
+ cname_table[cname_text] = packageName
+
+ return
+
+def getLibClasses(spdFile):
+
+ """Extract all the Lib Classes from an spd file."""
+
+ # Begin to parse the XML of the .spd
+ spd = xml.dom.minidom.parse(spdFile)
+
+ # Get the guid of the package
+ packageGuid = XmlElement(spd, "/PackageSurfaceArea/SpdHeader/GuidValue")
+
+ for libClass in XmlList(spd, "/PackageSurfaceArea/LibraryClassDeclarations/LibraryClass"):
+ className = XmlAttribute(libClass, "Name")
+ headerfile = XmlElementData(libClass.getElementsByTagName("IncludeHeader")[0])
+
+ packageRoot=os.path.dirname(spdFile)
+
+ headerfile = os.path.join(packageRoot, headerfile)
+
+ f = open(headerfile)
+
+ # This pattern can pick out function names if the EFI coding
+ # standard is followed. We could also use dumpbin on library
+ # instances to get a list of symbols.
+ functionPattern = re.compile("([_a-zA-Z][_a-zA-Z0-9]*) *\( *");
+
+ for line in f.readlines():
+ m = functionPattern.match(line)
+ if m:
+ functionName = m.group(1)
+ # Map it!
+ function_table[functionName] = (className, packageGuid)
+
+ f.close()
+
+def guid(strVal):
+ """Make a guid number out of a guid hex string."""
+ return long(strVal.replace('-',''), 16)
+
+# This acts like the main() function for the script, unless it is 'import'ed into another
+# script.
+if __name__ == '__main__':
+
+ # Create a pretty printer for dumping data structures in a readable form.
+ pp = pprint.PrettyPrinter(indent=2)
+
+ # Process the command line args.
+ optlist, args = getopt.getopt(sys.argv[1:], 'h', [ 'example-long-arg=', 'testing'])
+
+ """You should pass a file name as a paramter. It should be preprocessed text
+of all the .c and .h files in your module, which is cat'ed together into one
+large file."""
+
+ # Scrape out all the things that look like identifiers.
+ ids = getIdentifiers(args)
+
+ # Read in the spds from the workspace to find the Guids.
+ getSpds()
+
+ # Debug stuff.
+ print pp.pprint(function_table)
+ print pp.pprint(cname_table)
+ print "Classes = ", pp.pprint(list(search_classes(ids)))
+ print "C_Names = ", pp.pprint(list(search_cnames(ids)))