summaryrefslogtreecommitdiffstats
path: root/command.py
diff options
context:
space:
mode:
Diffstat (limited to 'command.py')
-rw-r--r--command.py84
1 files changed, 70 insertions, 14 deletions
diff --git a/command.py b/command.py
index b972a0be..12fe4172 100644
--- a/command.py
+++ b/command.py
@@ -61,13 +61,21 @@ class Command(object):
61 # it is the number of parallel jobs to default to. 61 # it is the number of parallel jobs to default to.
62 PARALLEL_JOBS = None 62 PARALLEL_JOBS = None
63 63
64 # Whether this command supports Multi-manifest. If False, then main.py will
65 # iterate over the manifests and invoke the command once per (sub)manifest.
66 # This is only checked after calling ValidateOptions, so that partially
67 # migrated subcommands can set it to False.
68 MULTI_MANIFEST_SUPPORT = True
69
64 def __init__(self, repodir=None, client=None, manifest=None, gitc_manifest=None, 70 def __init__(self, repodir=None, client=None, manifest=None, gitc_manifest=None,
65 git_event_log=None): 71 git_event_log=None, outer_client=None, outer_manifest=None):
66 self.repodir = repodir 72 self.repodir = repodir
67 self.client = client 73 self.client = client
74 self.outer_client = outer_client or client
68 self.manifest = manifest 75 self.manifest = manifest
69 self.gitc_manifest = gitc_manifest 76 self.gitc_manifest = gitc_manifest
70 self.git_event_log = git_event_log 77 self.git_event_log = git_event_log
78 self.outer_manifest = outer_manifest
71 79
72 # Cache for the OptionParser property. 80 # Cache for the OptionParser property.
73 self._optparse = None 81 self._optparse = None
@@ -135,6 +143,18 @@ class Command(object):
135 type=int, default=self.PARALLEL_JOBS, 143 type=int, default=self.PARALLEL_JOBS,
136 help=f'number of jobs to run in parallel (default: {default})') 144 help=f'number of jobs to run in parallel (default: {default})')
137 145
146 m = p.add_option_group('Multi-manifest options')
147 m.add_option('--outer-manifest', action='store_true',
148 help='operate starting at the outermost manifest')
149 m.add_option('--no-outer-manifest', dest='outer_manifest',
150 action='store_false', default=None,
151 help='do not operate on outer manifests')
152 m.add_option('--this-manifest-only', action='store_true', default=None,
153 help='only operate on this (sub)manifest')
154 m.add_option('--no-this-manifest-only', '--all-manifests',
155 dest='this_manifest_only', action='store_false',
156 help='operate on this manifest and its submanifests')
157
138 def _Options(self, p): 158 def _Options(self, p):
139 """Initialize the option parser with subcommand-specific options.""" 159 """Initialize the option parser with subcommand-specific options."""
140 160
@@ -252,16 +272,19 @@ class Command(object):
252 return project 272 return project
253 273
254 def GetProjects(self, args, manifest=None, groups='', missing_ok=False, 274 def GetProjects(self, args, manifest=None, groups='', missing_ok=False,
255 submodules_ok=False): 275 submodules_ok=False, all_manifests=False):
256 """A list of projects that match the arguments. 276 """A list of projects that match the arguments.
257 """ 277 """
258 if not manifest: 278 if all_manifests:
259 manifest = self.manifest 279 if not manifest:
260 all_projects_list = manifest.projects 280 manifest = self.manifest.outer_client
281 all_projects_list = manifest.all_projects
282 else:
283 if not manifest:
284 manifest = self.manifest
285 all_projects_list = manifest.projects
261 result = [] 286 result = []
262 287
263 mp = manifest.manifestProject
264
265 if not groups: 288 if not groups:
266 groups = manifest.GetGroupsStr() 289 groups = manifest.GetGroupsStr()
267 groups = [x for x in re.split(r'[,\s]+', groups) if x] 290 groups = [x for x in re.split(r'[,\s]+', groups) if x]
@@ -282,12 +305,19 @@ class Command(object):
282 for arg in args: 305 for arg in args:
283 # We have to filter by manifest groups in case the requested project is 306 # We have to filter by manifest groups in case the requested project is
284 # checked out multiple times or differently based on them. 307 # checked out multiple times or differently based on them.
285 projects = [project for project in manifest.GetProjectsWithName(arg) 308 projects = [project for project in manifest.GetProjectsWithName(
309 arg, all_manifests=all_manifests)
286 if project.MatchesGroups(groups)] 310 if project.MatchesGroups(groups)]
287 311
288 if not projects: 312 if not projects:
289 path = os.path.abspath(arg).replace('\\', '/') 313 path = os.path.abspath(arg).replace('\\', '/')
290 project = self._GetProjectByPath(manifest, path) 314 tree = manifest
315 if all_manifests:
316 # Look for the deepest matching submanifest.
317 for tree in reversed(list(manifest.all_manifests)):
318 if path.startswith(tree.topdir):
319 break
320 project = self._GetProjectByPath(tree, path)
291 321
292 # If it's not a derived project, update path->project mapping and 322 # If it's not a derived project, update path->project mapping and
293 # search again, as arg might actually point to a derived subproject. 323 # search again, as arg might actually point to a derived subproject.
@@ -308,7 +338,8 @@ class Command(object):
308 338
309 for project in projects: 339 for project in projects:
310 if not missing_ok and not project.Exists: 340 if not missing_ok and not project.Exists:
311 raise NoSuchProjectError('%s (%s)' % (arg, project.relpath)) 341 raise NoSuchProjectError('%s (%s)' % (
342 arg, project.RelPath(local=not all_manifests)))
312 if not project.MatchesGroups(groups): 343 if not project.MatchesGroups(groups):
313 raise InvalidProjectGroupsError(arg) 344 raise InvalidProjectGroupsError(arg)
314 345
@@ -319,12 +350,22 @@ class Command(object):
319 result.sort(key=_getpath) 350 result.sort(key=_getpath)
320 return result 351 return result
321 352
322 def FindProjects(self, args, inverse=False): 353 def FindProjects(self, args, inverse=False, all_manifests=False):
354 """Find projects from command line arguments.
355
356 Args:
357 args: a list of (case-insensitive) strings, projects to search for.
358 inverse: a boolean, if True, then projects not matching any |args| are
359 returned.
360 all_manifests: a boolean, if True then all manifests and submanifests are
361 used. If False, then only the local (sub)manifest is used.
362 """
323 result = [] 363 result = []
324 patterns = [re.compile(r'%s' % a, re.IGNORECASE) for a in args] 364 patterns = [re.compile(r'%s' % a, re.IGNORECASE) for a in args]
325 for project in self.GetProjects(''): 365 for project in self.GetProjects('', all_manifests=all_manifests):
366 paths = [project.name, project.RelPath(local=not all_manifests)]
326 for pattern in patterns: 367 for pattern in patterns:
327 match = pattern.search(project.name) or pattern.search(project.relpath) 368 match = any(pattern.search(x) for x in paths)
328 if not inverse and match: 369 if not inverse and match:
329 result.append(project) 370 result.append(project)
330 break 371 break
@@ -333,9 +374,24 @@ class Command(object):
333 else: 374 else:
334 if inverse: 375 if inverse:
335 result.append(project) 376 result.append(project)
336 result.sort(key=lambda project: project.relpath) 377 result.sort(key=lambda project: (project.manifest.path_prefix,
378 project.relpath))
337 return result 379 return result
338 380
381 def ManifestList(self, opt):
382 """Yields all of the manifests to traverse.
383
384 Args:
385 opt: The command options.
386 """
387 top = self.outer_manifest
388 if opt.outer_manifest is False or opt.this_manifest_only:
389 top = self.manifest
390 yield top
391 if not opt.this_manifest_only:
392 for child in top.all_children:
393 yield child
394
339 395
340class InteractiveCommand(Command): 396class InteractiveCommand(Command):
341 """Command which requires user interaction on the tty and 397 """Command which requires user interaction on the tty and