blob: d543e3a82f2287c03f60ff2e201af9cb3c3ea076 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
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
16import os
17import optparse
Conley Owensd21720d2012-04-16 11:02:21 -070018import platform
Colin Cross5acde752012-03-28 20:15:45 -070019import re
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import sys
21
22from error import NoSuchProjectError
Colin Cross5acde752012-03-28 20:15:45 -070023from error import InvalidProjectGroupsError
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024
25class Command(object):
26 """Base class for any command line action in repo.
27 """
28
29 common = False
30 manifest = None
31 _optparse = None
32
Shawn O. Pearcedb45da12009-04-18 13:49:13 -070033 def WantPager(self, opt):
34 return False
35
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070036 @property
37 def OptionParser(self):
38 if self._optparse is None:
39 try:
40 me = 'repo %s' % self.NAME
41 usage = self.helpUsage.strip().replace('%prog', me)
42 except AttributeError:
43 usage = 'repo %s' % self.NAME
44 self._optparse = optparse.OptionParser(usage = usage)
45 self._Options(self._optparse)
46 return self._optparse
47
48 def _Options(self, p):
49 """Initialize the option parser.
50 """
51
52 def Usage(self):
53 """Display usage and terminate.
54 """
55 self.OptionParser.print_usage()
56 sys.exit(1)
57
58 def Execute(self, opt, args):
59 """Perform the action, after option parsing is complete.
60 """
61 raise NotImplementedError
Conley Owens971de8e2012-04-16 10:36:08 -070062
Che-Liang Chiou69998b02012-01-11 11:28:42 +080063 def _ResetPathToProjectMap(self, projects):
64 self._by_path = dict((p.worktree, p) for p in projects)
65
66 def _UpdatePathToProjectMap(self, project):
67 self._by_path[project.worktree] = project
68
69 def _GetProjectByPath(self, path):
70 project = None
71 if os.path.exists(path):
72 oldpath = None
73 while path \
74 and path != oldpath \
75 and path != self.manifest.topdir:
76 try:
77 project = self._by_path[path]
78 break
79 except KeyError:
80 oldpath = path
81 path = os.path.dirname(path)
82 else:
83 try:
84 project = self._by_path[path]
85 except KeyError:
86 pass
87 return project
88
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070089 def GetProjects(self, args, missing_ok=False):
90 """A list of projects that match the arguments.
91 """
David Pursehouse8a68ff92012-09-24 12:15:13 +090092 all_projects = self.manifest.projects
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070093 result = []
94
Colin Cross5acde752012-03-28 20:15:45 -070095 mp = self.manifest.manifestProject
96
97 groups = mp.config.GetString('manifest.groups')
Colin Crossc39864f2012-04-23 13:41:58 -070098 if not groups:
Conley Owensbb1b5f52012-08-13 13:11:18 -070099 groups = 'all,-notdefault,platform-' + platform.system().lower()
Conley Owens971de8e2012-04-16 10:36:08 -0700100 groups = [x for x in re.split('[,\s]+', groups) if x]
Colin Cross5acde752012-03-28 20:15:45 -0700101
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700102 if not args:
Che-Liang Chiou69998b02012-01-11 11:28:42 +0800103 all_projects_list = all_projects.values()
104 derived_projects = []
105 for project in all_projects_list:
106 if project.Registered:
107 # Do not search registered subproject for derived projects
108 # since its parent has been searched already
109 continue
110 derived_projects.extend(project.GetDerivedSubprojects())
111 all_projects_list.extend(derived_projects)
112 for project in all_projects_list:
Colin Cross5acde752012-03-28 20:15:45 -0700113 if ((missing_ok or project.Exists) and
114 project.MatchesGroups(groups)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700115 result.append(project)
116 else:
Che-Liang Chiou69998b02012-01-11 11:28:42 +0800117 self._ResetPathToProjectMap(all_projects.values())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700118
119 for arg in args:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900120 project = all_projects.get(arg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700121
122 if not project:
Anthony Newnamdf14a702011-01-09 17:31:57 -0800123 path = os.path.abspath(arg).replace('\\', '/')
Che-Liang Chiou69998b02012-01-11 11:28:42 +0800124 project = self._GetProjectByPath(path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700125
Che-Liang Chiou69998b02012-01-11 11:28:42 +0800126 # If it's not a derived project, update path->project mapping and
127 # search again, as arg might actually point to a derived subproject.
128 if project and not project.Derived:
129 search_again = False
130 for subproject in project.GetDerivedSubprojects():
131 self._UpdatePathToProjectMap(subproject)
132 search_again = True
133 if search_again:
134 project = self._GetProjectByPath(path) or project
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135
136 if not project:
137 raise NoSuchProjectError(arg)
138 if not missing_ok and not project.Exists:
139 raise NoSuchProjectError(arg)
Colin Cross5acde752012-03-28 20:15:45 -0700140 if not project.MatchesGroups(groups):
141 raise InvalidProjectGroupsError(arg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700142
143 result.append(project)
144
145 def _getpath(x):
146 return x.relpath
147 result.sort(key=_getpath)
148 return result
149
David Pursehouse4f7bdea2012-10-22 12:50:15 +0900150# pylint: disable=W0223
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900151# Pylint warns that the `InteractiveCommand` and `PagedCommand` classes do not
152# override method `Execute` which is abstract in `Command`. Since that method
153# is always implemented in classes derived from `InteractiveCommand` and
154# `PagedCommand`, this warning can be suppressed.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700155class InteractiveCommand(Command):
156 """Command which requires user interaction on the tty and
157 must not run within a pager, even if the user asks to.
158 """
Shawn O. Pearcedb45da12009-04-18 13:49:13 -0700159 def WantPager(self, opt):
160 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700161
162class PagedCommand(Command):
163 """Command which defaults to output in a pager, as its
164 display tends to be larger than one screen full.
165 """
Shawn O. Pearcedb45da12009-04-18 13:49:13 -0700166 def WantPager(self, opt):
167 return True
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800168
David Pursehouse4f7bdea2012-10-22 12:50:15 +0900169# pylint: enable=W0223
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900170
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800171class MirrorSafeCommand(object):
172 """Command permits itself to run within a mirror,
173 and does not require a working directory.
174 """