From: bbahnsen Date: Wed, 22 Nov 2006 00:58:38 +0000 (+0000) Subject: Adding a python script dir and a script to calculate what dependencies a module has... X-Git-Tag: edk2-stable201903~23882 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=1a137e9826055eb8cfce77913e47e0d082ca27d0 Adding a python script dir and a script to calculate what dependencies a module has on library classes and guids. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@1995 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/Tools/Python/calcdeps.py b/Tools/Python/calcdeps.py new file mode 100644 index 0000000000..7742cf9cdc --- /dev/null +++ b/Tools/Python/calcdeps.py @@ -0,0 +1,237 @@ +#!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))) diff --git a/Tools/bin/build b/Tools/bin/build index fb9d78eb95..b8eee97dfb 100755 --- a/Tools/bin/build +++ b/Tools/bin/build @@ -9,4 +9,4 @@ # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. # -ant -logger org.tianocore.build.global.GenBuildLogger -f $WORKSPACE/build.xml $* +nice ant -logger org.tianocore.build.global.GenBuildLogger -f $WORKSPACE/build.xml $*