diff options
Diffstat (limited to 'subcmds/status.py')
| -rw-r--r-- | subcmds/status.py | 96 |
1 files changed, 39 insertions, 57 deletions
diff --git a/subcmds/status.py b/subcmds/status.py index 63972d72..5b669547 100644 --- a/subcmds/status.py +++ b/subcmds/status.py | |||
| @@ -1,5 +1,3 @@ | |||
| 1 | # -*- coding:utf-8 -*- | ||
| 2 | # | ||
| 3 | # Copyright (C) 2008 The Android Open Source Project | 1 | # Copyright (C) 2008 The Android Open Source Project |
| 4 | # | 2 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| @@ -14,25 +12,19 @@ | |||
| 14 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. | 13 | # limitations under the License. |
| 16 | 14 | ||
| 17 | from __future__ import print_function | 15 | import functools |
| 18 | |||
| 19 | from command import PagedCommand | ||
| 20 | |||
| 21 | try: | ||
| 22 | import threading as _threading | ||
| 23 | except ImportError: | ||
| 24 | import dummy_threading as _threading | ||
| 25 | |||
| 26 | import glob | 16 | import glob |
| 27 | 17 | import io | |
| 28 | import itertools | ||
| 29 | import os | 18 | import os |
| 30 | 19 | ||
| 20 | from command import DEFAULT_LOCAL_JOBS, PagedCommand | ||
| 21 | |||
| 31 | from color import Coloring | 22 | from color import Coloring |
| 32 | import platform_utils | 23 | import platform_utils |
| 33 | 24 | ||
| 25 | |||
| 34 | class Status(PagedCommand): | 26 | class Status(PagedCommand): |
| 35 | common = True | 27 | COMMON = True |
| 36 | helpSummary = "Show the working tree status" | 28 | helpSummary = "Show the working tree status" |
| 37 | helpUsage = """ | 29 | helpUsage = """ |
| 38 | %prog [<project>...] | 30 | %prog [<project>...] |
| @@ -84,36 +76,29 @@ the following meanings: | |||
| 84 | d: deleted ( in index, not in work tree ) | 76 | d: deleted ( in index, not in work tree ) |
| 85 | 77 | ||
| 86 | """ | 78 | """ |
| 79 | PARALLEL_JOBS = DEFAULT_LOCAL_JOBS | ||
| 87 | 80 | ||
| 88 | def _Options(self, p): | 81 | def _Options(self, p): |
| 89 | p.add_option('-j', '--jobs', | ||
| 90 | dest='jobs', action='store', type='int', default=2, | ||
| 91 | help="number of projects to check simultaneously") | ||
| 92 | p.add_option('-o', '--orphans', | 82 | p.add_option('-o', '--orphans', |
| 93 | dest='orphans', action='store_true', | 83 | dest='orphans', action='store_true', |
| 94 | help="include objects in working directory outside of repo projects") | 84 | help="include objects in working directory outside of repo projects") |
| 95 | p.add_option('-q', '--quiet', action='store_true', | ||
| 96 | help="only print the name of modified projects") | ||
| 97 | 85 | ||
| 98 | def _StatusHelper(self, project, clean_counter, sem, quiet): | 86 | def _StatusHelper(self, quiet, project): |
| 99 | """Obtains the status for a specific project. | 87 | """Obtains the status for a specific project. |
| 100 | 88 | ||
| 101 | Obtains the status for a project, redirecting the output to | 89 | Obtains the status for a project, redirecting the output to |
| 102 | the specified object. It will release the semaphore | 90 | the specified object. |
| 103 | when done. | ||
| 104 | 91 | ||
| 105 | Args: | 92 | Args: |
| 93 | quiet: Where to output the status. | ||
| 106 | project: Project to get status of. | 94 | project: Project to get status of. |
| 107 | clean_counter: Counter for clean projects. | 95 | |
| 108 | sem: Semaphore, will call release() when complete. | 96 | Returns: |
| 109 | output: Where to output the status. | 97 | The status of the project. |
| 110 | """ | 98 | """ |
| 111 | try: | 99 | buf = io.StringIO() |
| 112 | state = project.PrintWorkTreeStatus(quiet=quiet) | 100 | ret = project.PrintWorkTreeStatus(quiet=quiet, output_redir=buf) |
| 113 | if state == 'CLEAN': | 101 | return (ret, buf.getvalue()) |
| 114 | next(clean_counter) | ||
| 115 | finally: | ||
| 116 | sem.release() | ||
| 117 | 102 | ||
| 118 | def _FindOrphans(self, dirs, proj_dirs, proj_dirs_parents, outstring): | 103 | def _FindOrphans(self, dirs, proj_dirs, proj_dirs_parents, outstring): |
| 119 | """find 'dirs' that are present in 'proj_dirs_parents' but not in 'proj_dirs'""" | 104 | """find 'dirs' that are present in 'proj_dirs_parents' but not in 'proj_dirs'""" |
| @@ -126,34 +111,31 @@ the following meanings: | |||
| 126 | continue | 111 | continue |
| 127 | if item in proj_dirs_parents: | 112 | if item in proj_dirs_parents: |
| 128 | self._FindOrphans(glob.glob('%s/.*' % item) + | 113 | self._FindOrphans(glob.glob('%s/.*' % item) + |
| 129 | glob.glob('%s/*' % item), | 114 | glob.glob('%s/*' % item), |
| 130 | proj_dirs, proj_dirs_parents, outstring) | 115 | proj_dirs, proj_dirs_parents, outstring) |
| 131 | continue | 116 | continue |
| 132 | outstring.append(''.join([status_header, item, '/'])) | 117 | outstring.append(''.join([status_header, item, '/'])) |
| 133 | 118 | ||
| 134 | def Execute(self, opt, args): | 119 | def Execute(self, opt, args): |
| 135 | all_projects = self.GetProjects(args) | 120 | all_projects = self.GetProjects(args) |
| 136 | counter = itertools.count() | ||
| 137 | 121 | ||
| 138 | if opt.jobs == 1: | 122 | def _ProcessResults(_pool, _output, results): |
| 139 | for project in all_projects: | 123 | ret = 0 |
| 140 | state = project.PrintWorkTreeStatus(quiet=opt.quiet) | 124 | for (state, output) in results: |
| 125 | if output: | ||
| 126 | print(output, end='') | ||
| 141 | if state == 'CLEAN': | 127 | if state == 'CLEAN': |
| 142 | next(counter) | 128 | ret += 1 |
| 143 | else: | 129 | return ret |
| 144 | sem = _threading.Semaphore(opt.jobs) | 130 | |
| 145 | threads = [] | 131 | counter = self.ExecuteInParallel( |
| 146 | for project in all_projects: | 132 | opt.jobs, |
| 147 | sem.acquire() | 133 | functools.partial(self._StatusHelper, opt.quiet), |
| 148 | 134 | all_projects, | |
| 149 | t = _threading.Thread(target=self._StatusHelper, | 135 | callback=_ProcessResults, |
| 150 | args=(project, counter, sem, opt.quiet)) | 136 | ordered=True) |
| 151 | threads.append(t) | 137 | |
| 152 | t.daemon = True | 138 | if not opt.quiet and len(all_projects) == counter: |
| 153 | t.start() | ||
| 154 | for t in threads: | ||
| 155 | t.join() | ||
| 156 | if not opt.quiet and len(all_projects) == next(counter): | ||
| 157 | print('nothing to commit (working directory clean)') | 139 | print('nothing to commit (working directory clean)') |
| 158 | 140 | ||
| 159 | if opt.orphans: | 141 | if opt.orphans: |
| @@ -170,8 +152,8 @@ the following meanings: | |||
| 170 | class StatusColoring(Coloring): | 152 | class StatusColoring(Coloring): |
| 171 | def __init__(self, config): | 153 | def __init__(self, config): |
| 172 | Coloring.__init__(self, config, 'status') | 154 | Coloring.__init__(self, config, 'status') |
| 173 | self.project = self.printer('header', attr = 'bold') | 155 | self.project = self.printer('header', attr='bold') |
| 174 | self.untracked = self.printer('untracked', fg = 'red') | 156 | self.untracked = self.printer('untracked', fg='red') |
| 175 | 157 | ||
| 176 | orig_path = os.getcwd() | 158 | orig_path = os.getcwd() |
| 177 | try: | 159 | try: |
| @@ -179,11 +161,11 @@ the following meanings: | |||
| 179 | 161 | ||
| 180 | outstring = [] | 162 | outstring = [] |
| 181 | self._FindOrphans(glob.glob('.*') + | 163 | self._FindOrphans(glob.glob('.*') + |
| 182 | glob.glob('*'), | 164 | glob.glob('*'), |
| 183 | proj_dirs, proj_dirs_parents, outstring) | 165 | proj_dirs, proj_dirs_parents, outstring) |
| 184 | 166 | ||
| 185 | if outstring: | 167 | if outstring: |
| 186 | output = StatusColoring(self.manifest.globalConfig) | 168 | output = StatusColoring(self.client.globalConfig) |
| 187 | output.project('Objects not within a project (orphans)') | 169 | output.project('Objects not within a project (orphans)') |
| 188 | output.nl() | 170 | output.nl() |
| 189 | for entry in outstring: | 171 | for entry in outstring: |
