Home | History | Annotate | Download | only in win
      1 #!/usr/bin/env python
      2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 import copy
      7 import optparse
      8 
      9 __version__ = "1.0"
     10 
     11 # Constants used by Visual Studio.
     12 UNKNOWN_GUID = "{00000000-0000-0000-0000-000000000000}"
     13 FOLDER_GUID = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}"
     14 C_PROJECT_GUID = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"
     15 CSHARP_PROJECT_GUID = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"
     16 
     17 # A project defines it name, its guid and its dependencies.
     18 class Project:
     19   def __init__(self):
     20     self.name = ""
     21     self.type = UNKNOWN_GUID
     22     self.deps = []
     23 
     24   def __str__(self):
     25     return self.name
     26 
     27 
     28 def ScanSlnFile(filename):
     29   """Scan a Visual Studio .sln and extract the project dependencies."""
     30   try:
     31     sln = open(filename, "r")
     32   except IOError:
     33     sys.stderr.write("Unable to open " + filename + " for reading.\n")
     34     return 1
     35   projects = {}
     36   project = None
     37   while 1:
     38     line = sln.readline().strip()
     39     if not line:
     40       break
     41 
     42     if line.startswith('Project("{'):
     43       # Project definition line looks like
     44       # Project("$TypeGuid") = "$ProjectName", "$ProjectPath", "$ProjectGuid"$
     45       items = line.split('"')
     46       project = Project()
     47       project.name = items[3]
     48       project.path = items[5]
     49       project.guid = items[7]
     50       project.type = items[1]
     51       projects[items[7]] = project
     52 
     53     # Start of a dependency group.
     54     if line == "ProjectSection(ProjectDependencies) = postProject":
     55       line = sln.readline().strip()
     56       # End of a dependency group.
     57       while line and line != "EndProjectSection":
     58         project.deps.append(line[:len(project.guid)])
     59         line = sln.readline().strip()
     60 
     61   # We are done parsing.
     62   sln.close()
     63   return projects
     64 
     65 
     66 def sln_deps(filename, project_to_scan, reverse):
     67   """Displays the project's dependencies."""
     68   project_to_scan = project_to_scan.lower()
     69 
     70   projects = ScanSlnFile(filename)
     71 
     72   if reverse:
     73     # Inverse the dependencies map so they are displayed in the reverse order.
     74     # First, create a copy of the map.
     75     projects_reversed = copy.deepcopy(projects)
     76     for project_reversed in projects_reversed.itervalues():
     77       project_reversed.deps = []
     78     # Then, assign reverse dependencies.
     79     for project in projects.itervalues():
     80       for dep in project.deps:
     81         projects_reversed[dep].deps.append(project.guid)
     82     projects = projects_reversed
     83 
     84   # Print the results.
     85   for project in projects.itervalues():
     86     if project.type == FOLDER_GUID:
     87       continue
     88     if project_to_scan and project.name.lower() != project_to_scan:
     89       continue
     90     print project.name
     91     deps_name = [projects[d].name for d in project.deps]
     92     print "\n".join(str("  " + name) for name in sorted(deps_name,
     93                                                         key=str.lower))
     94   return 0
     95 
     96 
     97 def main():
     98   usage = "usage: %prog [options] solution [project]"
     99 
    100   description = ("Display the dependencies of a project in human readable"
    101                  " form. [project] is optional. If omited, all projects are"
    102                  " listed.")
    103 
    104   option_parser = optparse.OptionParser(usage = usage,
    105                                         version="%prog " + __version__,
    106                                         description = description)
    107   option_parser.add_option("-r",
    108                            "--reverse",
    109                            dest="reverse",
    110                            action="store_true",
    111                            default=False,
    112                            help="Display the reverse dependencies")
    113   options, args = option_parser.parse_args()
    114   if len(args) != 1 and len(args) != 2:
    115     option_parser.error("incorrect number of arguments")
    116 
    117   project_to_scan = ""
    118   if len(args) == 2:
    119     project_to_scan = args[1]
    120   return sln_deps(args[0], project_to_scan, options.reverse)
    121 
    122 
    123 if __name__ == '__main__':
    124   sys.exit(main())
    125