mirror of https://github.com/acidanthera/audk.git
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
This commit is contained in:
parent
205f41d551
commit
1a137e9826
|
@ -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)))
|
|
@ -9,4 +9,4 @@
|
||||||
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
# 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 $*
|
||||||
|
|
Loading…
Reference in New Issue