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[^ /\t\n\r\f\v>]+)(?P>)?' ) self.reEndElement = re.compile( r'\s*\S+)>' ) self.reAttribute = re.compile( r'\s*(?P\S+)="(?P.*?)"', 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( '' ) 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( '' ) 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