#!/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 # 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 XmlList(Dom, String): """Get a list of XML Elements using XPath style syntax.""" if Dom.nodeType==Dom.DOCUMENT_NODE: return XmlList(Dom.documentElement, String) if String[0] == "/": return XmlList(Dom, String[1:]) if String == "" : return [] TagList = String.split('/') nodes = [] if Dom.nodeType == Dom.ELEMENT_NODE and Dom.tagName.strip() == TagList[0]: if len(TagList) == 1: nodes = [Dom] else: restOfPath = "/".join(TagList[1:]) for child in Dom.childNodes: nodes = nodes + XmlList(child, restOfPath) return nodes def XmlElement (Dom, String): """Return a single element that matches the String which is XPath style syntax.""" try: return XmlList (Dom, String)[0].firstChild.data.strip(' ') except: return '' def XmlElementData (Dom): """Get the text for this element.""" return Dom.firstChild.data.strip(' ') def XmlAttribute (Dom, String): """Return a single attribute that named by String.""" try: return Dom.getAttribute(String) except: return '' 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 tag. cname_text = XmlElementData(cname) # Map the to the . 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)))