blob: 8c5e24616612bbcb203abf6a6fcea68cc2ee81a0 [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import os
18import optparse
Conley Owensd21720d2012-04-16 11:02:21 -070019import platform
Colin Cross5acde752012-03-28 20:15:45 -070020import re
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import sys
22
David Rileye0684ad2017-04-05 00:02:59 -070023from event_log import EventLog
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024from error import NoSuchProjectError
Colin Cross5acde752012-03-28 20:15:45 -070025from error import InvalidProjectGroupsError
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026
David Pursehouseb148ac92012-11-16 09:33:39 +090027
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070028class Command(object):
29 """Base class for any command line action in repo.
30 """
31
32 common = False
David Rileye0684ad2017-04-05 00:02:59 -070033 event_log = EventLog()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070034 manifest = None
35 _optparse = None
36
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -070037 def WantPager(self, _opt):
Shawn O. Pearcedb45da12009-04-18 13:49:13 -070038 return False
39
David Pursehouseb148ac92012-11-16 09:33:39 +090040 def ReadEnvironmentOptions(self, opts):
41 """ Set options from environment variables. """
42
43 env_options = self._RegisteredEnvironmentOptions()
44
45 for env_key, opt_key in env_options.items():
46 # Get the user-set option value if any
47 opt_value = getattr(opts, opt_key)
48
49 # If the value is set, it means the user has passed it as a command
50 # line option, and we should use that. Otherwise we can try to set it
51 # with the value from the corresponding environment variable.
52 if opt_value is not None:
53 continue
54
55 env_value = os.environ.get(env_key)
56 if env_value is not None:
57 setattr(opts, opt_key, env_value)
58
59 return opts
60
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070061 @property
62 def OptionParser(self):
63 if self._optparse is None:
64 try:
65 me = 'repo %s' % self.NAME
66 usage = self.helpUsage.strip().replace('%prog', me)
67 except AttributeError:
68 usage = 'repo %s' % self.NAME
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -070069 self._optparse = optparse.OptionParser(usage=usage)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070070 self._Options(self._optparse)
71 return self._optparse
72
73 def _Options(self, p):
74 """Initialize the option parser.
75 """
76
David Pursehouseb148ac92012-11-16 09:33:39 +090077 def _RegisteredEnvironmentOptions(self):
78 """Get options that can be set from environment variables.
79
80 Return a dictionary mapping environment variable name
81 to option key name that it can override.
82
83 Example: {'REPO_MY_OPTION': 'my_option'}
84
85 Will allow the option with key value 'my_option' to be set
86 from the value in the environment variable named 'REPO_MY_OPTION'.
87
88 Note: This does not work properly for options that are explicitly
89 set to None by the user, or options that are defined with a
90 default value other than None.
91
92 """
93 return {}
94
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070095 def Usage(self):
96 """Display usage and terminate.
97 """
98 self.OptionParser.print_usage()
99 sys.exit(1)
100
101 def Execute(self, opt, args):
102 """Perform the action, after option parsing is complete.
103 """
104 raise NotImplementedError
Conley Owens971de8e2012-04-16 10:36:08 -0700105
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800106 def _ResetPathToProjectMap(self, projects):
107 self._by_path = dict((p.worktree, p) for p in projects)
108
109 def _UpdatePathToProjectMap(self, project):
110 self._by_path[project.worktree] = project
111
Simran Basib9a1b732015-08-20 12:19:28 -0700112 def _GetProjectByPath(self, manifest, path):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800113 project = None
114 if os.path.exists(path):
115 oldpath = None
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700116 while path and \
117 path != oldpath and \
118 path != manifest.topdir:
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800119 try:
120 project = self._by_path[path]
121 break
122 except KeyError:
123 oldpath = path
124 path = os.path.dirname(path)
Mark E. Hamiltonf9fe3e12016-02-23 18:10:42 -0700125 if not project and path == manifest.topdir:
126 try:
127 project = self._by_path[path]
128 except KeyError:
129 pass
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800130 else:
131 try:
132 project = self._by_path[path]
133 except KeyError:
134 pass
135 return project
136
Simran Basib9a1b732015-08-20 12:19:28 -0700137 def GetProjects(self, args, manifest=None, groups='', missing_ok=False,
138 submodules_ok=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700139 """A list of projects that match the arguments.
140 """
Simran Basib9a1b732015-08-20 12:19:28 -0700141 if not manifest:
142 manifest = self.manifest
143 all_projects_list = manifest.projects
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700144 result = []
145
Simran Basib9a1b732015-08-20 12:19:28 -0700146 mp = manifest.manifestProject
Colin Cross5acde752012-03-28 20:15:45 -0700147
Graham Christensen0369a062015-07-29 17:02:54 -0500148 if not groups:
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700149 groups = mp.config.GetString('manifest.groups')
Colin Crossc39864f2012-04-23 13:41:58 -0700150 if not groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500151 groups = 'default,platform-' + platform.system().lower()
David Pursehouse1d947b32012-10-25 12:23:11 +0900152 groups = [x for x in re.split(r'[,\s]+', groups) if x]
Colin Cross5acde752012-03-28 20:15:45 -0700153
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700154 if not args:
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800155 derived_projects = {}
156 for project in all_projects_list:
157 if submodules_ok or project.sync_s:
158 derived_projects.update((p.name, p)
159 for p in project.GetDerivedSubprojects())
160 all_projects_list.extend(derived_projects.values())
161 for project in all_projects_list:
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700162 if (missing_ok or project.Exists) and project.MatchesGroups(groups):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700163 result.append(project)
164 else:
David James8d201162013-10-11 17:03:19 -0700165 self._ResetPathToProjectMap(all_projects_list)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700166
167 for arg in args:
Simran Basib9a1b732015-08-20 12:19:28 -0700168 projects = manifest.GetProjectsWithName(arg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700169
David James8d201162013-10-11 17:03:19 -0700170 if not projects:
Anthony Newnamdf14a702011-01-09 17:31:57 -0800171 path = os.path.abspath(arg).replace('\\', '/')
Simran Basib9a1b732015-08-20 12:19:28 -0700172 project = self._GetProjectByPath(manifest, path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700173
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800174 # If it's not a derived project, update path->project mapping and
175 # search again, as arg might actually point to a derived subproject.
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700176 if (project and not project.Derived and (submodules_ok or
177 project.sync_s)):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800178 search_again = False
179 for subproject in project.GetDerivedSubprojects():
180 self._UpdatePathToProjectMap(subproject)
181 search_again = True
182 if search_again:
Simran Basib9a1b732015-08-20 12:19:28 -0700183 project = self._GetProjectByPath(manifest, path) or project
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700184
David James8d201162013-10-11 17:03:19 -0700185 if project:
186 projects = [project]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700187
David James8d201162013-10-11 17:03:19 -0700188 if not projects:
189 raise NoSuchProjectError(arg)
190
191 for project in projects:
192 if not missing_ok and not project.Exists:
193 raise NoSuchProjectError(arg)
194 if not project.MatchesGroups(groups):
195 raise InvalidProjectGroupsError(arg)
196
197 result.extend(projects)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700198
199 def _getpath(x):
200 return x.relpath
201 result.sort(key=_getpath)
202 return result
203
Takeshi Kanemoto1f056442016-01-26 14:11:35 +0900204 def FindProjects(self, args, inverse=False):
Zhiguang Lia8864fb2013-03-15 10:32:10 +0800205 result = []
David Pursehouse84c4d3c2013-04-30 10:57:37 +0900206 patterns = [re.compile(r'%s' % a, re.IGNORECASE) for a in args]
Zhiguang Lia8864fb2013-03-15 10:32:10 +0800207 for project in self.GetProjects(''):
David Pursehouse84c4d3c2013-04-30 10:57:37 +0900208 for pattern in patterns:
Takeshi Kanemoto1f056442016-01-26 14:11:35 +0900209 match = pattern.search(project.name) or pattern.search(project.relpath)
210 if not inverse and match:
Zhiguang Lia8864fb2013-03-15 10:32:10 +0800211 result.append(project)
212 break
Takeshi Kanemoto1f056442016-01-26 14:11:35 +0900213 if inverse and match:
214 break
215 else:
216 if inverse:
217 result.append(project)
Zhiguang Lia8864fb2013-03-15 10:32:10 +0800218 result.sort(key=lambda project: project.relpath)
219 return result
220
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700221
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700222class InteractiveCommand(Command):
223 """Command which requires user interaction on the tty and
224 must not run within a pager, even if the user asks to.
225 """
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700226 def WantPager(self, _opt):
Shawn O. Pearcedb45da12009-04-18 13:49:13 -0700227 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700228
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700229
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230class PagedCommand(Command):
231 """Command which defaults to output in a pager, as its
232 display tends to be larger than one screen full.
233 """
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700234 def WantPager(self, _opt):
Shawn O. Pearcedb45da12009-04-18 13:49:13 -0700235 return True
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800236
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700237
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800238class MirrorSafeCommand(object):
239 """Command permits itself to run within a mirror,
240 and does not require a working directory.
241 """
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700242
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700243
Dan Willemsen79360642015-08-31 15:45:06 -0700244class GitcAvailableCommand(object):
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700245 """Command that requires GITC to be available, but does
246 not require the local client to be a GITC client.
247 """
Dan Willemsen79360642015-08-31 15:45:06 -0700248
Mark E. Hamilton8ccfa742016-02-10 10:44:30 -0700249
Dan Willemsen79360642015-08-31 15:45:06 -0700250class GitcClientCommand(object):
251 """Command that requires the local client to be a GITC
252 client.
253 """