command.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #
  2. # Copyright (C) 2008 The Android Open Source Project
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import os
  16. import optparse
  17. import platform
  18. import re
  19. import sys
  20. from error import NoSuchProjectError
  21. from error import InvalidProjectGroupsError
  22. class Command(object):
  23. """Base class for any command line action in repo.
  24. """
  25. common = False
  26. manifest = None
  27. _optparse = None
  28. def WantPager(self, opt):
  29. return False
  30. @property
  31. def OptionParser(self):
  32. if self._optparse is None:
  33. try:
  34. me = 'repo %s' % self.NAME
  35. usage = self.helpUsage.strip().replace('%prog', me)
  36. except AttributeError:
  37. usage = 'repo %s' % self.NAME
  38. self._optparse = optparse.OptionParser(usage = usage)
  39. self._Options(self._optparse)
  40. return self._optparse
  41. def _Options(self, p):
  42. """Initialize the option parser.
  43. """
  44. def Usage(self):
  45. """Display usage and terminate.
  46. """
  47. self.OptionParser.print_usage()
  48. sys.exit(1)
  49. def Execute(self, opt, args):
  50. """Perform the action, after option parsing is complete.
  51. """
  52. raise NotImplementedError
  53. def _ResetPathToProjectMap(self, projects):
  54. self._by_path = dict((p.worktree, p) for p in projects)
  55. def _UpdatePathToProjectMap(self, project):
  56. self._by_path[project.worktree] = project
  57. def _GetProjectByPath(self, path):
  58. project = None
  59. if os.path.exists(path):
  60. oldpath = None
  61. while path \
  62. and path != oldpath \
  63. and path != self.manifest.topdir:
  64. try:
  65. project = self._by_path[path]
  66. break
  67. except KeyError:
  68. oldpath = path
  69. path = os.path.dirname(path)
  70. else:
  71. try:
  72. project = self._by_path[path]
  73. except KeyError:
  74. pass
  75. return project
  76. def GetProjects(self, args, missing_ok=False):
  77. """A list of projects that match the arguments.
  78. """
  79. all_projects = self.manifest.projects
  80. result = []
  81. mp = self.manifest.manifestProject
  82. groups = mp.config.GetString('manifest.groups')
  83. if not groups:
  84. groups = 'all,-notdefault,platform-' + platform.system().lower()
  85. groups = [x for x in re.split('[,\s]+', groups) if x]
  86. if not args:
  87. all_projects_list = all_projects.values()
  88. derived_projects = []
  89. for project in all_projects_list:
  90. if project.Registered:
  91. # Do not search registered subproject for derived projects
  92. # since its parent has been searched already
  93. continue
  94. derived_projects.extend(project.GetDerivedSubprojects())
  95. all_projects_list.extend(derived_projects)
  96. for project in all_projects_list:
  97. if ((missing_ok or project.Exists) and
  98. project.MatchesGroups(groups)):
  99. result.append(project)
  100. else:
  101. self._ResetPathToProjectMap(all_projects.values())
  102. for arg in args:
  103. project = all_projects.get(arg)
  104. if not project:
  105. path = os.path.abspath(arg).replace('\\', '/')
  106. project = self._GetProjectByPath(path)
  107. # If it's not a derived project, update path->project mapping and
  108. # search again, as arg might actually point to a derived subproject.
  109. if project and not project.Derived:
  110. search_again = False
  111. for subproject in project.GetDerivedSubprojects():
  112. self._UpdatePathToProjectMap(subproject)
  113. search_again = True
  114. if search_again:
  115. project = self._GetProjectByPath(path) or project
  116. if not project:
  117. raise NoSuchProjectError(arg)
  118. if not missing_ok and not project.Exists:
  119. raise NoSuchProjectError(arg)
  120. if not project.MatchesGroups(groups):
  121. raise InvalidProjectGroupsError(arg)
  122. result.append(project)
  123. def _getpath(x):
  124. return x.relpath
  125. result.sort(key=_getpath)
  126. return result
  127. # pylint: disable-msg=W0223
  128. # Pylint warns that the `InteractiveCommand` and `PagedCommand` classes do not
  129. # override method `Execute` which is abstract in `Command`. Since that method
  130. # is always implemented in classes derived from `InteractiveCommand` and
  131. # `PagedCommand`, this warning can be suppressed.
  132. class InteractiveCommand(Command):
  133. """Command which requires user interaction on the tty and
  134. must not run within a pager, even if the user asks to.
  135. """
  136. def WantPager(self, opt):
  137. return False
  138. class PagedCommand(Command):
  139. """Command which defaults to output in a pager, as its
  140. display tends to be larger than one screen full.
  141. """
  142. def WantPager(self, opt):
  143. return True
  144. # pylint: enable-msg=W0223
  145. class MirrorSafeCommand(object):
  146. """Command permits itself to run within a mirror,
  147. and does not require a working directory.
  148. """