• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
6import copy
7import optparse
8
9__version__ = "1.0"
10
11# Constants used by Visual Studio.
12UNKNOWN_GUID = "{00000000-0000-0000-0000-000000000000}"
13FOLDER_GUID = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}"
14C_PROJECT_GUID = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"
15CSHARP_PROJECT_GUID = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"
16
17# A project defines it name, its guid and its dependencies.
18class 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
28def 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
66def 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
97def 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
123if __name__ == '__main__':
124  sys.exit(main())
125