summaryrefslogtreecommitdiff
path: root/devtools/bin/vsdotnetxmlparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/bin/vsdotnetxmlparser.py')
-rw-r--r--devtools/bin/vsdotnetxmlparser.py218
1 files changed, 218 insertions, 0 deletions
diff --git a/devtools/bin/vsdotnetxmlparser.py b/devtools/bin/vsdotnetxmlparser.py
new file mode 100644
index 0000000..e9e643a
--- /dev/null
+++ b/devtools/bin/vsdotnetxmlparser.py
@@ -0,0 +1,218 @@
+
+import re
+import os, ezxmlfile
+
+
+# Until I find a better XML parser, this one acts like xml.parsers.expat but it preserves the \r's and \n's inside
+# attribute strings in .vcproj files.
+class VSDotNetXMLParserError:
+ pass
+
+
+def GetLineNumber( data, filePos ):
+ lines = data.split( '\n' )
+ testOffset = 0
+ for i,x in enumerate( lines ):
+ testOffset += len(x) + 1
+ if testOffset >= filePos:
+ return i+1
+ return -1
+
+
+verbose = 0
+
+class VSDotNetXMLParser:
+ def __init__( self ):
+ self.XmlDeclHandler = None
+ self.StartElementHandler = None
+ self.EndElementHandler = None
+
+ self.reStartElement = re.compile( r'\s*<(?P<blockName>[^ /\t\n\r\f\v>]+)(?P<noAttrs>>)?' )
+ self.reEndElement = re.compile( r'\s*</(?P<blockName>\S+)>' )
+ self.reAttribute = re.compile( r'\s*(?P<attrName>\S+)="(?P<attrValue>.*?)"', re.DOTALL )
+ self.reEndAttributes = re.compile( r'\s*>' )
+ self.reEndAttributesNoSubElements = re.compile( r'\s*(\/|\?)>' )
+
+ def Parse( self, data, final ):
+ curFilePos = 0
+ elementDepth = 0
+ self.__bReadXMLHeader = 0
+
+ # First read the XML header.
+ while 1:
+ m = self.reStartElement.match( data, curFilePos )
+ if m:
+ curFilePos = m.end()
+
+ # Read the element name and get all its attributes.
+ elementName = m.group('blockName')
+ attributes = []
+
+ # No attributes?
+ if m.group('noAttrs') == '>':
+ elementDepth += 1
+ self.__CallElementHandler( elementName, [], 0 )
+ continue
+
+ if verbose:
+ print 'elem: ' + elementName
+
+ while 1:
+ m = self.reAttribute.match( data, curFilePos )
+ if m:
+ if verbose:
+ print 'attr: %s, value: %s' % (m.group('attrName'), m.group('attrValue'))
+ curFilePos = m.end()
+ attributes.append( m.group('attrName') )
+ attributes.append( m.group('attrValue') )
+ continue
+
+ m = self.reEndAttributesNoSubElements.match( data, curFilePos )
+ if m:
+ if verbose:
+ print 'endattr'
+ curFilePos = m.end()
+ self.__CallElementHandler( elementName, attributes, 1 )
+ break
+
+ m = self.reEndAttributes.match( data, curFilePos )
+ if m:
+ if verbose:
+ print 'endattr2'
+ curFilePos = m.end()
+ elementDepth += 1
+ self.__CallElementHandler( elementName, attributes, 0 )
+ break
+ else:
+ raise VSDotNetXMLParserError
+
+ else:
+ m = self.reEndElement.match( data, curFilePos )
+ if m:
+ if verbose:
+ print 'endelem'
+ curFilePos = m.end()
+ elementDepth -= 1
+ self.EndElementHandler( '<end element name not supported>' )
+ else:
+ # When we're done with the file, the depth should be 0.
+ if elementDepth != 0:
+ print 'line %d, depth: %d' % (GetLineNumber( data, curFilePos ), elementDepth)
+ raise VSDotNetXMLParserError
+ break
+
+ # Must at least have a header!
+ if not self.__bReadXMLHeader:
+ raise VSDotNetXMLParserError
+
+
+ def __CallElementHandler( self, elementName, attributes, bEnd ):
+ if self.__bReadXMLHeader:
+ self.StartElementHandler( elementName, attributes )
+ if bEnd:
+ self.EndElementHandler( '<end element name not supported>' )
+ else:
+ # First element must be the XML header.
+ if elementName != '?xml' or not bEnd:
+ raise VSDotNetXMLParserError
+
+ versionString = encodingString = None
+ for(i,a) in enumerate( attributes ):
+ if (i & 1) == 0:
+ if a == 'version':
+ versionString = attributes[i+1]
+ elif a == 'encoding':
+ encodingString = attributes[i+1]
+
+ if not versionString or not encodingString:
+ raise VSDotNetXMLParserError
+
+ self.XmlDeclHandler( versionString, encodingString, 1 )
+ self.__bReadXMLHeader = 1
+
+
+
+def LoadVCProj( filename ):
+ f = open( filename, 'rb' )
+ return ezxmlfile.EZXMLFile( f.read() )
+
+
+def FindInList( theList, elem ):
+ for i,val in enumerate( theList ):
+ if val == elem:
+ return i
+ return -1
+
+
+def IsExcludedFromProjects( e, validProjects ):
+ for c in e.Children:
+ if c.Name == "FileConfiguration":
+ if FindInList( validProjects, c.GetAttributeValue( 'Name' ) ) != -1:
+ if c.GetAttributeValue( 'ExcludedFromBuild' ) == 'TRUE':
+ return 1
+ return 0
+
+
+def StripConfigBlocks_R( e, validProjects ):
+ newChildren = []
+
+ # Strip out unwanted configuration blocks.
+ if e.Name == 'Configuration' or e.Name == 'FileConfiguration':
+ bValid = 0
+ v = e.GetAttributeValue( 'Name' )
+ for p in validProjects:
+ if p == v:
+ bValid = 1
+ break
+
+ if not bValid:
+ return 0
+
+ # Strip out files that are excluded from the validProjects.
+ if e.Name == "File":
+ if IsExcludedFromProjects( e, validProjects ):
+ return 0
+
+
+ # Recurse..
+ newChildren = []
+ for child in e.Children:
+ if StripConfigBlocks_R( child, validProjects ):
+ newChildren.append( child )
+ e.Children = newChildren
+ return 1
+
+
+def RemoveEmptyFilterBlocks_R( e ):
+ if e.Name == "Filter" and len( e.Children ) == 0:
+ return 0
+
+ # Recurse..
+ newChildren = []
+ for child in e.Children:
+ if RemoveEmptyFilterBlocks_R( child ):
+ newChildren.append( child )
+ e.Children = newChildren
+ return 1
+
+
+def WriteSeparateVCProj( f, validProjects, outFilename ):
+ outFile = open( outFilename, 'wb' )
+
+ # Make a copy of f so we're not trashing its data.
+ #f = copy.deepcopy( f )
+
+ # Strip out the source control crap.
+ e = f.GetElement( 'VisualStudioProject' )
+ e.RemoveAttribute( 'SccProjectName' )
+ e.RemoveAttribute( 'SccAuxPath' )
+ e.RemoveAttribute( 'SccLocalPath' )
+ e.RemoveAttribute( 'SccProvider' )
+
+ # Now strip out blocks that are for
+ StripConfigBlocks_R( f.RootElement, validProjects )
+ RemoveEmptyFilterBlocks_R( f.RootElement )
+
+ f.WriteFile( outFile )
+ outFile.close()
+ print "Wrote %s" % outFilename