1c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org#!/usr/bin/env python
2ea13fb0eee7f81ce6037b95339566fa0fb9d7bd9thomasvl@chromium.org
346d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com# Copyright (c) 2012 Google Inc. All rights reserved.
4ea13fb0eee7f81ce6037b95339566fa0fb9d7bd9thomasvl@chromium.org# Use of this source code is governed by a BSD-style license that can be
5ea13fb0eee7f81ce6037b95339566fa0fb9d7bd9thomasvl@chromium.org# found in the LICENSE file.
64787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
74787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org"""Make the format of a vcproj really pretty.
84787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
94787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org   This script normalize and sort an xml. It also fetches all the properties
104787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org   inside linked vsprops and include them explicitly in the vcproj.
11ea13fb0eee7f81ce6037b95339566fa0fb9d7bd9thomasvl@chromium.org
124787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org   It outputs the resulting xml to stdout.
134787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org"""
144787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
154787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org__author__ = 'nsylvain (Nicolas Sylvain)'
164787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
174787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgimport os
184787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgimport sys
194787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
204787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgfrom xml.dom.minidom import parse
214787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgfrom xml.dom.minidom import Node
224787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
234787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgREPLACEMENTS = dict()
24ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.orgARGUMENTS = None
254787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
26c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
27c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.orgclass CmpTuple(object):
284787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  """Compare function between 2 tuple."""
294787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  def __call__(self, x, y):
3046d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com    return cmp(x[0], y[0])
314787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
32c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
33c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.orgclass CmpNode(object):
344787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  """Compare function between 2 xml nodes."""
354787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
3646d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com  def __call__(self, x, y):
3746d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com    def get_string(node):
3846d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      node_string = "node"
3946d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      node_string += node.nodeName
4046d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      if node.nodeValue:
4146d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com        node_string += node.nodeValue
424787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
4346d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      if node.attributes:
4446d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com        # We first sort by name, if present.
4546d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com        node_string += node.getAttribute("Name")
464787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
4746d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com        all_nodes = []
4846d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com        for (name, value) in node.attributes.items():
4946d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com          all_nodes.append((name, value))
504787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
5146d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com        all_nodes.sort(CmpTuple())
5246d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com        for (name, value) in all_nodes:
5346d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com          node_string += name
5446d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com          node_string += value
554787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
5646d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      return node_string
574787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
5846d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com    return cmp(get_string(x), get_string(y))
594787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
60c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
614787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgdef PrettyPrintNode(node, indent=0):
624787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if node.nodeType == Node.TEXT_NODE:
634787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    if node.data.strip():
644787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      print '%s%s' % (' '*indent, node.data.strip())
654787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    return
664787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
674787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if node.childNodes:
684787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    node.normalize()
694787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Get the number of attributes
704787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  attr_count = 0
714787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if node.attributes:
724787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    attr_count = node.attributes.length
734787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
744787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Print the main tag
754787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if attr_count == 0:
764787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    print '%s<%s>' % (' '*indent, node.nodeName)
774787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  else:
784787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    print '%s<%s' % (' '*indent, node.nodeName)
794787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
804787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    all_attributes = []
814787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    for (name, value) in node.attributes.items():
824787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      all_attributes.append((name, value))
834787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      all_attributes.sort(CmpTuple())
844787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    for (name, value) in all_attributes:
854787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      print '%s  %s="%s"' % (' '*indent, name, value)
864787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    print '%s>' % (' '*indent)
874787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if node.nodeValue:
884787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    print '%s  %s' % (' '*indent, node.nodeValue)
894787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
904787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  for sub_node in node.childNodes:
914787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    PrettyPrintNode(sub_node, indent=indent+2)
924787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  print '%s</%s>' % (' '*indent, node.nodeName)
937531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org
94c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
957531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.orgdef FlattenFilter(node):
967531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org  """Returns a list of all the node and sub nodes."""
977531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org  node_list = []
987531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org
997531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org  if (node.attributes and
1007531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org      node.getAttribute('Name') == '_excluded_files'):
1017531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org      # We don't add the "_excluded_files" filter.
1027531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    return []
1037531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org
1047531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org  for current in node.childNodes:
1057531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    if current.nodeName == 'Filter':
1067531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org      node_list.extend(FlattenFilter(current))
1077531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    else:
1087531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org      node_list.append(current)
1097531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org
1107531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org  return node_list
1117531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org
112c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
113ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.orgdef FixFilenames(filenames, current_directory):
114ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org  new_list = []
115ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org  for filename in filenames:
116ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org    if filename:
117ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org      for key in REPLACEMENTS:
118ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org        filename = filename.replace(key, REPLACEMENTS[key])
119ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org      os.chdir(current_directory)
120ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org      filename = filename.strip('"\' ')
121ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org      if filename.startswith('$'):
122ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org        new_list.append(filename)
123ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org      else:
124ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org        new_list.append(os.path.abspath(filename))
125ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org  return new_list
126ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org
127c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
128ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.orgdef AbsoluteNode(node):
129c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org  """Makes all the properties we know about in this node absolute."""
130ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org  if node.attributes:
131ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org    for (name, value) in node.attributes.items():
132ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org      if name in ['InheritedPropertySheets', 'RelativePath',
133ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org                  'AdditionalIncludeDirectories',
134ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org                  'IntermediateDirectory', 'OutputDirectory',
135ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org                  'AdditionalLibraryDirectories']:
136ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org        # We want to fix up these paths
137ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org        path_list = value.split(';')
138ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org        new_list = FixFilenames(path_list, os.path.dirname(ARGUMENTS[1]))
139ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org        node.setAttribute(name, ';'.join(new_list))
140ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org      if not value:
141ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org        node.removeAttribute(name)
142ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org
143c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
1444787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgdef CleanupVcproj(node):
145c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org  """For each sub node, we call recursively this function."""
1464787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  for sub_node in node.childNodes:
147ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org    AbsoluteNode(sub_node)
1484787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    CleanupVcproj(sub_node)
1494787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
1504787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Normalize the node, and remove all extranous whitespaces.
1514787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  for sub_node in node.childNodes:
1524787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    if sub_node.nodeType == Node.TEXT_NODE:
15346d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      sub_node.data = sub_node.data.replace("\r", "")
15446d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      sub_node.data = sub_node.data.replace("\n", "")
15546d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      sub_node.data = sub_node.data.rstrip()
156ea13fb0eee7f81ce6037b95339566fa0fb9d7bd9thomasvl@chromium.org
1577531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org  # Fix all the semicolon separated attributes to be sorted, and we also
1587531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org  # remove the dups.
1594787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if node.attributes:
1604787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    for (name, value) in node.attributes.items():
1617531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org      sorted_list = sorted(value.split(';'))
162ea13fb0eee7f81ce6037b95339566fa0fb9d7bd9thomasvl@chromium.org      unique_list = []
16346d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      for i in sorted_list:
16446d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com        if not unique_list.count(i):
16546d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com          unique_list.append(i)
1667531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org      node.setAttribute(name, ';'.join(unique_list))
1674787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      if not value:
1684787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org        node.removeAttribute(name)
1694787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
1704787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if node.childNodes:
1714787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    node.normalize()
172ea13fb0eee7f81ce6037b95339566fa0fb9d7bd9thomasvl@chromium.org
1734787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # For each node, take a copy, and remove it from the list.
1744787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  node_array = []
1754787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  while node.childNodes and node.childNodes[0]:
1767531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    # Take a copy of the node and remove it from the list.
1777531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    current = node.childNodes[0]
1787531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    node.removeChild(current)
1797531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org
1807531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    # If the child is a filter, we want to append all its children
1817531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    # to this same list.
1827531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    if current.nodeName == 'Filter':
1837531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org      node_array.extend(FlattenFilter(current))
1847531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org    else:
1857531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org      node_array.append(current)
1867531c1c00b3bdb40dcb9407052fa03d01b9d605fnsylvain@chromium.org
187ea13fb0eee7f81ce6037b95339566fa0fb9d7bd9thomasvl@chromium.org
1884787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Sort the list.
1894787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  node_array.sort(CmpNode())
1904787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
1914787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Insert the nodes in the correct order.
1924787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  for new_node in node_array:
1934787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # But don't append empty tool node.
1944787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    if new_node.nodeName == 'Tool':
1954787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      if new_node.attributes and new_node.attributes.length == 1:
1964787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org        # This one was empty.
1974787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org        continue
198ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org    if new_node.nodeName == 'UserMacro':
199ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org      continue
2004787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    node.appendChild(new_node)
2014787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
202c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
2034787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgdef GetConfiguationNodes(vcproj):
2044787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  #TODO(nsylvain): Find a better way to navigate the xml.
2054787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  nodes = []
2064787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  for node in vcproj.childNodes:
2074787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    if node.nodeName == "Configurations":
2084787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      for sub_node in node.childNodes:
2094787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org        if sub_node.nodeName == "Configuration":
2104787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org          nodes.append(sub_node)
2114787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2124787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  return nodes
2134787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
214c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
2154787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgdef GetChildrenVsprops(filename):
2164787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  dom = parse(filename)
2174787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if dom.documentElement.attributes:
2184787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    vsprops = dom.documentElement.getAttribute('InheritedPropertySheets')
2194787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    return FixFilenames(vsprops.split(';'), os.path.dirname(filename))
2204787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  return []
2214787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2224787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgdef SeekToNode(node1, child2):
2234787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # A text node does not have properties.
2244787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if child2.nodeType == Node.TEXT_NODE:
2254787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    return None
2264787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2274787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Get the name of the current node.
2284787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  current_name = child2.getAttribute("Name")
2294787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if not current_name:
2304787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # There is no name. We don't know how to merge.
2314787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    return None
2324787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2334787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Look through all the nodes to find a match.
2344787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  for sub_node in node1.childNodes:
2354787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    if sub_node.nodeName == child2.nodeName:
2364787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      name = sub_node.getAttribute("Name")
2374787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      if name == current_name:
2384787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org        return sub_node
2394787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2404787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # No match. We give up.
2414787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  return None
2424787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
243c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
2444787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgdef MergeAttributes(node1, node2):
2454787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # No attributes to merge?
2464787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  if not node2.attributes:
2474787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    return
2484787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2494787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  for (name, value2) in node2.attributes.items():
2504787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # Don't merge the 'Name' attribute.
2514787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    if name == 'Name':
2524787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      continue
2534787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    value1 = node1.getAttribute(name)
2544787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    if value1:
2554787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      # The attribute exist in the main node. If it's equal, we leave it
2564787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      # untouched, otherwise we concatenate it.
2574787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      if value1 != value2:
2584787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org        node1.setAttribute(name, ';'.join([value1, value2]))
2594787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    else:
2604787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      # The attribute does nto exist in the main node. We append this one.
2614787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      node1.setAttribute(name, value2)
2624787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2634787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # If the attribute was a property sheet attributes, we remove it, since
2644787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # they are useless.
2654787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    if name == 'InheritedPropertySheets':
2664787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      node1.removeAttribute(name)
2674787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
268c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
2694787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgdef MergeProperties(node1, node2):
2704787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  MergeAttributes(node1, node2)
2714787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  for child2 in node2.childNodes:
2724787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    child1 = SeekToNode(node1, child2)
2734787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    if child1:
2744787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      MergeProperties(child1, child2)
2754787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    else:
2764787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      node1.appendChild(child2.cloneNode(True))
2774787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
278c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
279ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.orgdef main(argv):
28046d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com  """Main function of this vcproj prettifier."""
281ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org  global ARGUMENTS
282ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org  ARGUMENTS = argv
2834787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2844787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # check if we have exactly 1 parameter.
285ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org  if len(argv) < 2:
2864787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    print ('Usage: %s "c:\\path\\to\\vcproj.vcproj" [key1=value1] '
287ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org           '[key2=value2]' % argv[0])
288c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org    return 1
2894787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2904787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Parse the keys
291ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org  for i in range(2, len(argv)):
292ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org    (key, value) = argv[i].split('=')
2934787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    REPLACEMENTS[key] = value
2944787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2954787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Open the vcproj and parse the xml.
296ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org  dom = parse(argv[1])
2974787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
2984787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # First thing we need to do is find the Configuration Node and merge them
2994787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # with the vsprops they include.
3004787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  for configuration_node in GetConfiguationNodes(dom.documentElement):
3014787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # Get the property sheets associated with this configuration.
3024787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    vsprops = configuration_node.getAttribute('InheritedPropertySheets')
3034787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
3044787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # Fix the filenames to be absolute.
3054787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    vsprops_list = FixFilenames(vsprops.strip().split(';'),
306ff3b11917354598c00dd7bef08e851e3add8985cnsylvain@chromium.org                                os.path.dirname(argv[1]))
3074787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
3084787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # Extend the list of vsprops with all vsprops contained in the current
3094787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # vsprops.
3104787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    for current_vsprops in vsprops_list:
31146d34d835a9fb416adc83e8cf4aba29204bece0bbradnelson@google.com      vsprops_list.extend(GetChildrenVsprops(current_vsprops))
3124787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
3134787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    # Now that we have all the vsprops, we need to merge them.
3144787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org    for current_vsprops in vsprops_list:
3154787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org      MergeProperties(configuration_node,
3164787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org                      parse(current_vsprops).documentElement)
3174787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
3184787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Now that everything is merged, we need to cleanup the xml.
3194787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  CleanupVcproj(dom.documentElement)
3204787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org
3214787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # Finally, we use the prett xml function to print the vcproj back to the
3224787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  # user.
3234787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  #print dom.toprettyxml(newl="\n")
3244787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.org  PrettyPrintNode(dom.documentElement)
325c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org  return 0
326c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org
327ea13fb0eee7f81ce6037b95339566fa0fb9d7bd9thomasvl@chromium.org
3284787a79516a69bdfa3e5255d116a03d513d2b61ansylvain@chromium.orgif __name__ == '__main__':
329c1ced77af959d11dd80252ab471e89906ea70f09maruel@chromium.org  sys.exit(main(sys.argv))
330