Adding a python script dir and a script to calculate what dependencies a module has...
authorbbahnsen <bbahnsen@6f19259b-4bc3-4df7-8a09-765794883524>
Wed, 22 Nov 2006 00:58:38 +0000 (00:58 +0000)
committerbbahnsen <bbahnsen@6f19259b-4bc3-4df7-8a09-765794883524>
Wed, 22 Nov 2006 00:58:38 +0000 (00:58 +0000)
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@1995 6f19259b-4bc3-4df7-8a09-765794883524

Tools/Python/calcdeps.py [new file with mode: 0644]
Tools/bin/build

diff --git a/Tools/Python/calcdeps.py b/Tools/Python/calcdeps.py
new file mode 100644 (file)
index 0000000..7742cf9
--- /dev/null
@@ -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 <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)))
index fb9d78e..b8eee97 100755 (executable)
@@ -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 $*