blob: dc6052a70d58bcf65e6973330a2b73691eb70c00 [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
David Pursehouseb148ac92012-11-16 09:33:39 +090025
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026class Command(object):
27 """Base class for any command line action in repo.
28 """
29
30 common = False
31 manifest = None
32 _optparse = None
33
Shawn O. Pearcedb45da12009-04-18 13:49:13 -070034 def WantPager(self, opt):
35 return False
36
David Pursehouseb148ac92012-11-16 09:33:39 +090037 def ReadEnvironmentOptions(self, opts):
38 """ Set options from environment variables. """
39
40 env_options = self._RegisteredEnvironmentOptions()
41
42 for env_key, opt_key in env_options.items():
43 # Get the user-set option value if any
44 opt_value = getattr(opts, opt_key)
45
46 # If the value is set, it means the user has passed it as a command
47 # line option, and we should use that. Otherwise we can try to set it
48 # with the value from the corresponding environment variable.
49 if opt_value is not None:
50 continue
51
52 env_value = os.environ.get(env_key)
53 if env_value is not None:
54 setattr(opts, opt_key, env_value)
55
56 return opts
57
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070058 @property
59 def OptionParser(self):
60 if self._optparse is None:
61 try:
62 me = 'repo %s' % self.NAME
63 usage = self.helpUsage.strip().replace('%prog', me)
64 except AttributeError:
65 usage = 'repo %s' % self.NAME
66 self._optparse = optparse.OptionParser(usage = usage)
67 self._Options(self._optparse)
68 return self._optparse
69
70 def _Options(self, p):
71 """Initialize the option parser.
72 """
73
David Pursehouseb148ac92012-11-16 09:33:39 +090074 def _RegisteredEnvironmentOptions(self):
75 """Get options that can be set from environment variables.
76
77 Return a dictionary mapping environment variable name
78 to option key name that it can override.
79
80 Example: {'REPO_MY_OPTION': 'my_option'}
81
82 Will allow the option with key value 'my_option' to be set
83 from the value in the environment variable named 'REPO_MY_OPTION'.
84
85 Note: This does not work properly for options that are explicitly
86 set to None by the user, or options that are defined with a
87 default value other than None.
88
89 """
90 return {}
91
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070092 def Usage(self):
93 """Display usage and terminate.
94 """
95 self.OptionParser.print_usage()
96 sys.exit(1)
97
98 def Execute(self, opt, args):
99 """Perform the action, after option parsing is complete.
100 """
101 raise NotImplementedError
Conley Owens971de8e2012-04-16 10:36:08 -0700102
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700103 def GetProjects(self, args, missing_ok=False):
104 """A list of projects that match the arguments.
105 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900106 all_projects = self.manifest.projects
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700107 result = []
108
Colin Cross5acde752012-03-28 20:15:45 -0700109 mp = self.manifest.manifestProject
110
111 groups = mp.config.GetString('manifest.groups')
Colin Crossc39864f2012-04-23 13:41:58 -0700112 if not groups:
Conley Owensbb1b5f52012-08-13 13:11:18 -0700113 groups = 'all,-notdefault,platform-' + platform.system().lower()
David Pursehouse1d947b32012-10-25 12:23:11 +0900114 groups = [x for x in re.split(r'[,\s]+', groups) if x]
Colin Cross5acde752012-03-28 20:15:45 -0700115
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700116 if not args:
Shawn O. Pearcecd81dd62012-10-26 12:18:00 -0700117 for project in all_projects.values():
Colin Cross5acde752012-03-28 20:15:45 -0700118 if ((missing_ok or project.Exists) and
119 project.MatchesGroups(groups)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700120 result.append(project)
121 else:
Shawn O. Pearcecd81dd62012-10-26 12:18:00 -0700122 by_path = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700123
124 for arg in args:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900125 project = all_projects.get(arg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700126
127 if not project:
Anthony Newnamdf14a702011-01-09 17:31:57 -0800128 path = os.path.abspath(arg).replace('\\', '/')
Shawn O. Pearcecd81dd62012-10-26 12:18:00 -0700129
130 if not by_path:
131 by_path = dict()
132 for p in all_projects.values():
133 by_path[p.worktree] = p
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700134
Shawn O. Pearcecd81dd62012-10-26 12:18:00 -0700135 if os.path.exists(path):
136 oldpath = None
137 while path \
138 and path != oldpath \
139 and path != self.manifest.topdir:
140 try:
141 project = by_path[path]
142 break
143 except KeyError:
144 oldpath = path
145 path = os.path.dirname(path)
146 else:
147 try:
148 project = by_path[path]
149 except KeyError:
150 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700151
152 if not project:
153 raise NoSuchProjectError(arg)
154 if not missing_ok and not project.Exists:
155 raise NoSuchProjectError(arg)
Colin Cross5acde752012-03-28 20:15:45 -0700156 if not project.MatchesGroups(groups):
157 raise InvalidProjectGroupsError(arg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158
159 result.append(project)
160
161 def _getpath(x):
162 return x.relpath
163 result.sort(key=_getpath)
164 return result
165
David Pursehouse4f7bdea2012-10-22 12:50:15 +0900166# pylint: disable=W0223
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900167# Pylint warns that the `InteractiveCommand` and `PagedCommand` classes do not
168# override method `Execute` which is abstract in `Command`. Since that method
169# is always implemented in classes derived from `InteractiveCommand` and
170# `PagedCommand`, this warning can be suppressed.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171class InteractiveCommand(Command):
172 """Command which requires user interaction on the tty and
173 must not run within a pager, even if the user asks to.
174 """
Shawn O. Pearcedb45da12009-04-18 13:49:13 -0700175 def WantPager(self, opt):
176 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700177
178class PagedCommand(Command):
179 """Command which defaults to output in a pager, as its
180 display tends to be larger than one screen full.
181 """
Shawn O. Pearcedb45da12009-04-18 13:49:13 -0700182 def WantPager(self, opt):
183 return True
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800184
David Pursehouse4f7bdea2012-10-22 12:50:15 +0900185# pylint: enable=W0223
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900186
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800187class MirrorSafeCommand(object):
188 """Command permits itself to run within a mirror,
189 and does not require a working directory.
190 """