-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
git-svn-id: http://gyp.googlecode.com/svn/trunk@139 78cadc50-ecff-11dd-a971-7dbc132099af
- Loading branch information
1 parent
8ae5f27
commit 4787a79
Showing
2 changed files
with
286 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
pretty_vcproj: | ||
Usage: pretty_vcproj.py "c:\path\to\vcproj.vcproj" [key1=value1] [key2=value2] | ||
|
||
They key/value pair are used to resolve vsprops name. | ||
|
||
For example, if I want to diff the base.vcproj project: | ||
|
||
pretty_vcproj.py z:\dev\src-chrome\src\base\build\base.vcproj "$(SolutionDir)=z:\dev\src-chrome\src\chrome\\" "$(CHROMIUM_BUILD)=" "$(CHROME_BUILD_TYPE)=" > orignal.txt | ||
pretty_vcproj.py z:\dev\src-chrome\src\base\base_gyp.vcproj "$(SolutionDir)=z:\dev\src-chrome\src\chrome\\" "$(CHROMIUM_BUILD)=" "$(CHROME_BUILD_TYPE)=" > gyp.txt | ||
|
||
And you can use your favorite diff tool to see the changes. | ||
|
||
Note: In the case of base.vcproj, the original vcproj is one level up the generated one. | ||
I suggest you do a search and replace for '"..\' and replace it with '"' in original.txt | ||
before you perform the diff. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
#!/usr/bin/python2.5 | ||
# Copyright 2009 Google Inc. | ||
# All Rights Reserved. | ||
|
||
"""Make the format of a vcproj really pretty. | ||
This script normalize and sort an xml. It also fetches all the properties | ||
inside linked vsprops and include them explicitly in the vcproj. | ||
It outputs the resulting xml to stdout. | ||
""" | ||
|
||
__author__ = 'nsylvain (Nicolas Sylvain)' | ||
|
||
import os | ||
import sys | ||
|
||
from xml.dom.minidom import parse | ||
from xml.dom.minidom import Node | ||
|
||
REPLACEMENTS = dict() | ||
|
||
class CmpTuple: | ||
"""Compare function between 2 tuple.""" | ||
def __call__(self, x, y): | ||
(key1, value1) = x | ||
(key2, value2) = y | ||
return cmp(key1, key2) | ||
|
||
class CmpNode: | ||
"""Compare function between 2 xml nodes.""" | ||
|
||
def get_string(self, node): | ||
node_string = "node" | ||
node_string += node.nodeName | ||
if node.nodeValue: | ||
node_string += node.nodeValue | ||
|
||
if node.attributes: | ||
# We first sort by name, if present. | ||
node_string += node.getAttribute("Name") | ||
|
||
all_nodes = [] | ||
for (name, value) in node.attributes.items(): | ||
all_nodes.append((name, value)) | ||
|
||
all_nodes.sort(CmpTuple()) | ||
for (name, value) in all_nodes: | ||
node_string += name | ||
node_string += value | ||
|
||
return node_string | ||
|
||
def __call__(self, x, y): | ||
return cmp(self.get_string(x), self.get_string(y)) | ||
|
||
def PrettyPrintNode(node, indent=0): | ||
if node.nodeType == Node.TEXT_NODE: | ||
if node.data.strip(): | ||
print '%s%s' % (' '*indent, node.data.strip()) | ||
return | ||
|
||
if node.childNodes: | ||
node.normalize() | ||
# Get the number of attributes | ||
attr_count = 0 | ||
if node.attributes: | ||
attr_count = node.attributes.length | ||
|
||
# Print the main tag | ||
if attr_count == 0: | ||
print '%s<%s>' % (' '*indent, node.nodeName) | ||
else: | ||
print '%s<%s' % (' '*indent, node.nodeName) | ||
|
||
all_attributes = [] | ||
for (name, value) in node.attributes.items(): | ||
all_attributes.append((name, value)) | ||
all_attributes.sort(CmpTuple()) | ||
for (name, value) in all_attributes: | ||
print '%s %s="%s"' % (' '*indent, name, value) | ||
print '%s>' % (' '*indent) | ||
if node.nodeValue: | ||
print '%s %s' % (' '*indent, node.nodeValue) | ||
|
||
for sub_node in node.childNodes: | ||
PrettyPrintNode(sub_node, indent=indent+2) | ||
print '%s</%s>' % (' '*indent, node.nodeName) | ||
|
||
def CleanupVcproj(node): | ||
# For each sub node, we call recursively this function. | ||
for sub_node in node.childNodes: | ||
CleanupVcproj(sub_node) | ||
|
||
# Normalize the node, and remove all extranous whitespaces. | ||
for sub_node in node.childNodes: | ||
if sub_node.nodeType == Node.TEXT_NODE: | ||
sub_node.data = sub_node.data.replace("\r", "") | ||
sub_node.data = sub_node.data.replace("\n", "") | ||
sub_node.data = sub_node.data.rstrip() | ||
|
||
# Fix all the semicolon separated attributes to be sorted. | ||
if node.attributes: | ||
for (name, value) in node.attributes.items(): | ||
node.setAttribute(name, ';'.join(sorted(value.split(';')))) | ||
if not value: | ||
node.removeAttribute(name) | ||
|
||
if node.childNodes: | ||
node.normalize() | ||
|
||
# For each node, take a copy, and remove it from the list. | ||
node_array = [] | ||
while node.childNodes and node.childNodes[0]: | ||
node_array.append(node.childNodes[0]) | ||
node.removeChild(node.childNodes[0]) | ||
|
||
# Sort the list. | ||
node_array.sort(CmpNode()) | ||
|
||
# Insert the nodes in the correct order. | ||
for new_node in node_array: | ||
# But don't append empty tool node. | ||
if new_node.nodeName == 'Tool': | ||
if new_node.attributes and new_node.attributes.length == 1: | ||
# This one was empty. | ||
continue | ||
# We also don't add the "_excluded_files" filter. | ||
if new_node.nodeName == 'Filter': | ||
if (new_node.attributes and | ||
new_node.getAttribute('Name') == '_excluded_files'): | ||
continue | ||
node.appendChild(new_node) | ||
|
||
def GetConfiguationNodes(vcproj): | ||
#TODO(nsylvain): Find a better way to navigate the xml. | ||
nodes = [] | ||
for node in vcproj.childNodes: | ||
if node.nodeName == "Configurations": | ||
for sub_node in node.childNodes: | ||
if sub_node.nodeName == "Configuration": | ||
nodes.append(sub_node) | ||
|
||
return nodes | ||
|
||
def FixFilenames(filenames, current_directory): | ||
replacements = REPLACEMENTS | ||
#{ | ||
# '$(SolutionDir)': 'z:\\dev\\src-chrome\\src\\chrome\\', | ||
# '$(CHROMIUM_BUILD)' :'', | ||
# '$(CHROME_BUILD_TYPE)' :'', | ||
# } | ||
|
||
new_list = [] | ||
for filename in filenames: | ||
if filename: | ||
for key in replacements: | ||
filename = filename.replace(key, replacements[key]) | ||
os.chdir(current_directory) | ||
new_list.append(os.path.abspath(filename)) | ||
return new_list | ||
|
||
def GetChildrenVsprops(filename): | ||
dom = parse(filename) | ||
if dom.documentElement.attributes: | ||
vsprops = dom.documentElement.getAttribute('InheritedPropertySheets') | ||
return FixFilenames(vsprops.split(';'), os.path.dirname(filename)) | ||
return [] | ||
|
||
def SeekToNode(node1, child2): | ||
# A text node does not have properties. | ||
if child2.nodeType == Node.TEXT_NODE: | ||
return None | ||
|
||
# Get the name of the current node. | ||
current_name = child2.getAttribute("Name") | ||
if not current_name: | ||
# There is no name. We don't know how to merge. | ||
return None | ||
|
||
# Look through all the nodes to find a match. | ||
for sub_node in node1.childNodes: | ||
if sub_node.nodeName == child2.nodeName: | ||
name = sub_node.getAttribute("Name") | ||
if name == current_name: | ||
return sub_node | ||
|
||
# No match. We give up. | ||
return None | ||
|
||
def MergeAttributes(node1, node2): | ||
# No attributes to merge? | ||
if not node2.attributes: | ||
return | ||
|
||
for (name, value2) in node2.attributes.items(): | ||
# Don't merge the 'Name' attribute. | ||
if name == 'Name': | ||
continue | ||
value1 = node1.getAttribute(name) | ||
if value1: | ||
# The attribute exist in the main node. If it's equal, we leave it | ||
# untouched, otherwise we concatenate it. | ||
if value1 != value2: | ||
node1.setAttribute(name, ';'.join([value1, value2])) | ||
else: | ||
# The attribute does nto exist in the main node. We append this one. | ||
node1.setAttribute(name, value2) | ||
|
||
# If the attribute was a property sheet attributes, we remove it, since | ||
# they are useless. | ||
if name == 'InheritedPropertySheets': | ||
node1.removeAttribute(name) | ||
|
||
def MergeProperties(node1, node2): | ||
MergeAttributes(node1, node2) | ||
for child2 in node2.childNodes: | ||
child1 = SeekToNode(node1, child2) | ||
if child1: | ||
MergeProperties(child1, child2) | ||
else: | ||
node1.appendChild(child2.cloneNode(True)) | ||
|
||
def main(): | ||
"""Main function of this vcproj prettifier.""" | ||
|
||
# check if we have exactly 1 parameter. | ||
if len(sys.argv) < 2: | ||
print ('Usage: %s "c:\\path\\to\\vcproj.vcproj" [key1=value1] ' | ||
'[key2=value2]' % sys.argv[0]) | ||
return | ||
|
||
# Parse the keys | ||
for i in range(2, len(sys.argv)): | ||
print sys.argv[i].split('=') | ||
(key, value) = sys.argv[i].split('=') | ||
REPLACEMENTS[key] = value | ||
|
||
# Open the vcproj and parse the xml. | ||
dom = parse(sys.argv[1]) | ||
|
||
# First thing we need to do is find the Configuration Node and merge them | ||
# with the vsprops they include. | ||
for configuration_node in GetConfiguationNodes(dom.documentElement): | ||
# Get the property sheets associated with this configuration. | ||
vsprops = configuration_node.getAttribute('InheritedPropertySheets') | ||
|
||
# Fix the filenames to be absolute. | ||
vsprops_list = FixFilenames(vsprops.strip().split(';'), | ||
os.path.dirname(sys.argv[1])) | ||
|
||
# Extend the list of vsprops with all vsprops contained in the current | ||
# vsprops. | ||
for current_vsprops in vsprops_list: | ||
vsprops_list.extend(GetChildrenVsprops(current_vsprops)) | ||
|
||
# Now that we have all the vsprops, we need to merge them. | ||
for current_vsprops in vsprops_list: | ||
MergeProperties(configuration_node, | ||
parse(current_vsprops).documentElement) | ||
|
||
# Now that everything is merged, we need to cleanup the xml. | ||
CleanupVcproj(dom.documentElement) | ||
|
||
# Finally, we use the prett xml function to print the vcproj back to the | ||
# user. | ||
#print dom.toprettyxml(newl="\n") | ||
PrettyPrintNode(dom.documentElement) | ||
|
||
if __name__ == '__main__': | ||
main() |