blob: 3482946dd5d1c8bf5c7c57a724fb6327adfda2a0 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Mike Frysingeracf63b22019-06-13 02:24:21 -040015import http.cookiejar as cookielib
Anthony King85b24ac2014-05-06 15:57:48 +010016import json
David Pursehouse86d973d2012-08-24 10:21:02 +090017import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070018from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
20import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070021import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import subprocess
23import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070024import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070025import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040026import urllib.error
27import urllib.parse
28import urllib.request
29import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030
Roy Lee18afd7f2010-05-09 04:32:08 +080031try:
32 import threading as _threading
33except ImportError:
34 import dummy_threading as _threading
35
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070036try:
37 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090038
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070039 def _rlimit_nofile():
40 return resource.getrlimit(resource.RLIMIT_NOFILE)
41except ImportError:
42 def _rlimit_nofile():
43 return (256, 256)
44
Dave Borowitz18857212012-10-23 17:02:59 -070045try:
46 import multiprocessing
47except ImportError:
48 multiprocessing = None
49
David Rileye0684ad2017-04-05 00:02:59 -070050import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070051from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090052from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090053from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070054import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070055from project import Project
56from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080057from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000058from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070059import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070060from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070061from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080062from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070063from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070064
Dave Borowitz67700e92012-10-23 15:00:54 -070065_ONE_DAY_S = 24 * 60 * 60
66
David Pursehouse819827a2020-02-12 15:20:19 +090067
Doug Andersonfc06ced2011-03-16 15:49:18 -070068class _FetchError(Exception):
69 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
70 pass
71
David Pursehouse819827a2020-02-12 15:20:19 +090072
Xin Li745be2e2019-06-03 11:24:30 -070073class _CheckoutError(Exception):
74 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
75
David Pursehouse819827a2020-02-12 15:20:19 +090076
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080077class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080078 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070079 common = True
80 helpSummary = "Update working tree to the latest revision"
81 helpUsage = """
82%prog [...]
83"""
84 helpDescription = """
85The '%prog' command synchronizes local project directories
86with the remote repositories specified in the manifest. If a local
87project does not yet exist, it will clone a new local directory from
88the remote repository and set up tracking branches as specified in
89the manifest. If the local project already exists, '%prog'
90will update the remote branches and rebase any new local changes
91on top of the new remote changes.
92
93'%prog' will synchronize all projects listed at the command
94line. Projects can be specified either by name, or by a relative
95or absolute path to the project's local directory. If no projects
96are specified, '%prog' will synchronize all projects listed in
97the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070098
99The -d/--detach option can be used to switch specified projects
100back to the manifest revision. This option is especially helpful
101if the project is currently on a topic branch, but the manifest
102revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700103
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700104The -s/--smart-sync option can be used to sync to a known good
105build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200106manifest. The -t/--smart-tag option is similar and allows you to
107specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700108
David Pursehousecf76b1b2012-09-14 10:31:42 +0900109The -u/--manifest-server-username and -p/--manifest-server-password
110options can be used to specify a username and password to authenticate
111with the manifest server when using the -s or -t option.
112
113If -u and -p are not specified when using the -s or -t option, '%prog'
114will attempt to read authentication credentials for the manifest server
115from the user's .netrc file.
116
117'%prog' will not use authentication credentials from -u/-p or .netrc
118if the manifest server specified in the manifest file already includes
119credentials.
120
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400121By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400122to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500123
Kevin Degiabaa7f32014-11-12 11:27:45 -0700124The --force-sync option can be used to overwrite existing git
125directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900126object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700127refs may be removed when overwriting.
128
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500129The --force-remove-dirty option can be used to remove previously used
130projects with uncommitted changes. WARNING: This may cause data to be
131lost since uncommitted changes may be removed with projects that no longer
132exist in the manifest.
133
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700134The --no-clone-bundle option disables any attempt to use
135$URL/clone.bundle to bootstrap a new Git repository from a
136resumeable bundle file on a content delivery network. This
137may be necessary if there are problems with the local Python
138HTTP client or proxy configuration, but the Git binary works.
139
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800140The --fetch-submodules option enables fetching Git submodules
141of a project from server.
142
David Pursehousef2fad612015-01-29 14:36:28 +0900143The -c/--current-branch option can be used to only fetch objects that
144are on the branch specified by a project's revision.
145
David Pursehouseb1553542014-09-04 21:28:09 +0900146The --optimized-fetch option can be used to only fetch projects that
147are fixed to a sha1 revision if the sha1 revision does not already
148exist locally.
149
David Pursehouse74cfd272015-10-14 10:50:15 +0900150The --prune option can be used to remove any refs that no longer
151exist on the remote.
152
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400153# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700154
155If at least one project remote URL uses an SSH connection (ssh://,
156git+ssh://, or user@host:path syntax) repo will automatically
157enable the SSH ControlMaster option when connecting to that host.
158This feature permits other projects in the same '%prog' session to
159reuse the same SSH tunnel, saving connection setup overheads.
160
161To disable this behavior on UNIX platforms, set the GIT_SSH
162environment variable to 'ssh'. For example:
163
164 export GIT_SSH=ssh
165 %prog
166
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400167# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700168
169This feature is automatically disabled on Windows, due to the lack
170of UNIX domain socket support.
171
172This feature is not compatible with url.insteadof rewrites in the
173user's ~/.gitconfig. '%prog' is currently not able to perform the
174rewrite early enough to establish the ControlMaster tunnel.
175
176If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
177later is required to fix a server side protocol bug.
178
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700179"""
180
Nico Sallembien6623b212010-05-11 12:57:01 -0700181 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000182 try:
183 self.jobs = self.manifest.default.sync_j
184 except ManifestParseError:
185 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700186
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500187 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200188 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400189 help='obsolete option (to be deleted in the future)')
190 p.add_option('--fail-fast',
191 dest='fail_fast', action='store_true',
192 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700193 p.add_option('--force-sync',
194 dest='force_sync', action='store_true',
195 help="overwrite an existing git directory if it needs to "
196 "point to a different object directory. WARNING: this "
197 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500198 p.add_option('--force-remove-dirty',
199 dest='force_remove_dirty', action='store_true',
200 help="force remove projects with uncommitted modifications if "
201 "projects no longer exist in the manifest. "
202 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900203 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700204 dest='local_only', action='store_true',
205 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900206 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100207 dest='mp_update', action='store_false', default='true',
208 help='use the existing manifest checkout as-is. '
209 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700211 dest='network_only', action='store_true',
212 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700214 dest='detach_head', action='store_true',
215 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900216 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700217 dest='current_branch_only', action='store_true',
218 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500219 p.add_option('-v', '--verbose',
220 dest='output_mode', action='store_true',
221 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900222 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500223 dest='output_mode', action='store_false',
224 help='only show errors')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900225 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800226 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700227 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500228 p.add_option('-m', '--manifest-name',
229 dest='manifest_name',
230 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700231 p.add_option('--clone-bundle', action='store_true',
232 help='enable use of /clone.bundle on HTTP/HTTPS')
233 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700234 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800235 p.add_option('-u', '--manifest-server-username', action='store',
236 dest='manifest_server_username',
237 help='username to authenticate with the manifest server')
238 p.add_option('-p', '--manifest-server-password', action='store',
239 dest='manifest_server_password',
240 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800241 p.add_option('--fetch-submodules',
242 dest='fetch_submodules', action='store_true',
243 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700244 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500245 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700246 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900247 p.add_option('--optimized-fetch',
248 dest='optimized_fetch', action='store_true',
249 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600250 p.add_option('--retry-fetches',
251 default=0, action='store', type='int',
252 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900253 p.add_option('--prune', dest='prune', action='store_true',
254 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700255 if show_smart:
256 p.add_option('-s', '--smart-sync',
257 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900258 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200259 p.add_option('-t', '--smart-tag',
260 dest='smart_tag', action='store',
261 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700262
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700263 g = p.add_option_group('repo Version options')
264 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500265 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700266 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700267 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800268 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700269 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500271 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700272 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800273
David James8d201162013-10-11 17:03:19 -0700274 Delegates most of the work to _FetchHelper.
275
276 Args:
277 opt: Program options returned from optparse. See _Options().
278 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500279 sem: We'll release() this semaphore when we exit so that another thread
280 can be started up.
David James89ece422014-01-09 18:51:58 -0800281 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700282 _FetchHelper docstring for details.
283 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500284 try:
285 for project in projects:
286 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400287 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500288 break
289 finally:
290 sem.release()
David James8d201162013-10-11 17:03:19 -0700291
Xin Li745be2e2019-06-03 11:24:30 -0700292 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
293 clone_filter):
David James8d201162013-10-11 17:03:19 -0700294 """Fetch git objects for a single project.
295
David Pursehousec1b86a22012-11-14 11:36:51 +0900296 Args:
297 opt: Program options returned from optparse. See _Options().
298 project: Project object for the project to fetch.
299 lock: Lock for accessing objects that are shared amongst multiple
300 _FetchHelper() threads.
301 fetched: set object that we will add project.gitdir to when we're done
302 (with our lock held).
303 pm: Instance of a Project object. We will call pm.update() (with our
304 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900305 err_event: We'll set this event in the case of an error (after printing
306 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700307 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700308
309 Returns:
310 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900311 """
312 # We'll set to true once we've locked the lock.
313 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700314
David Pursehousec1b86a22012-11-14 11:36:51 +0900315 # Encapsulate everything in a try/except/finally so that:
316 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900317 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700318 start = time.time()
319 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900320 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700321 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900322 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900323 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500324 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900325 current_branch_only=opt.current_branch_only,
326 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500327 clone_bundle=opt.clone_bundle,
328 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900329 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600330 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900331 prune=opt.prune,
332 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900333 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700334
David Pursehousec1b86a22012-11-14 11:36:51 +0900335 # Lock around all the rest of the code, since printing, updating a set
336 # and Progress.update() are not thread safe.
337 lock.acquire()
338 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700339
David Pursehousec1b86a22012-11-14 11:36:51 +0900340 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800341 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700342 print('error: Cannot fetch %s from %s'
343 % (project.name, project.remote.url),
344 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400345 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900346 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700347
David Pursehousec1b86a22012-11-14 11:36:51 +0900348 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400349 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900350 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800351 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400352 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900353 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900354 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900355 err_event.set()
356 raise
357 finally:
358 if did_lock:
359 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700360 finish = time.time()
361 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
362 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800363
David James8d201162013-10-11 17:03:19 -0700364 return success
365
Mike Frysinger5a033082019-09-23 19:21:20 -0400366 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700367 fetched = set()
David James89ece422014-01-09 18:51:58 -0800368 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200369 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200370 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800371
David James89ece422014-01-09 18:51:58 -0800372 objdir_project_map = dict()
373 for project in projects:
374 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700375
David James89ece422014-01-09 18:51:58 -0800376 threads = set()
377 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800378 for project_list in objdir_project_map.values():
379 # Check for any errors before running any more tasks.
380 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400381 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800382 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700383
David James89ece422014-01-09 18:51:58 -0800384 sem.acquire()
385 kwargs = dict(opt=opt,
386 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500387 sem=sem,
David James89ece422014-01-09 18:51:58 -0800388 lock=lock,
389 fetched=fetched,
390 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700391 err_event=err_event,
392 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800393 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900394 t = _threading.Thread(target=self._FetchProjectList,
395 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200396 # Ensure that Ctrl-C will not freeze the repo process.
397 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800398 threads.add(t)
399 t.start()
David James89ece422014-01-09 18:51:58 -0800400 else:
401 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800402
David James89ece422014-01-09 18:51:58 -0800403 for t in threads:
404 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800405
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700406 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700407 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700408
Julien Campergue335f5ef2013-10-16 11:02:35 +0200409 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400410 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200411
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700412 return fetched
413
Xin Li745be2e2019-06-03 11:24:30 -0700414 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
415 """Main function of the fetch threads.
416
417 Delegates most of the work to _CheckoutOne.
418
419 Args:
420 opt: Program options returned from optparse. See _Options().
421 projects: Projects to fetch.
422 sem: We'll release() this semaphore when we exit so that another thread
423 can be started up.
424 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
425 _CheckoutOne docstring for details.
426 """
427 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400428 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700429 finally:
430 sem.release()
431
Vadim Bendeburydff91942019-11-06 11:05:00 -0800432 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700433 """Checkout work tree for one project
434
435 Args:
436 opt: Program options returned from optparse. See _Options().
437 project: Project object for the project to checkout.
438 lock: Lock for accessing objects that are shared amongst multiple
439 _CheckoutWorker() threads.
440 pm: Instance of a Project object. We will call pm.update() (with our
441 lock held).
442 err_event: We'll set this event in the case of an error (after printing
443 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800444 err_results: A list of strings, paths to git repos where checkout
445 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700446
447 Returns:
448 Whether the fetch was successful.
449 """
450 # We'll set to true once we've locked the lock.
451 did_lock = False
452
Xin Li745be2e2019-06-03 11:24:30 -0700453 # Encapsulate everything in a try/except/finally so that:
454 # - We always set err_event in the case of an exception.
455 # - We always make sure we unlock the lock if we locked it.
456 start = time.time()
457 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
458 detach_head=opt.detach_head)
459 success = False
460 try:
461 try:
462 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700463
464 # Lock around all the rest of the code, since printing, updating a set
465 # and Progress.update() are not thread safe.
466 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400467 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700468 did_lock = True
469
470 if not success:
471 err_event.set()
472 print('error: Cannot checkout %s' % (project.name),
473 file=sys.stderr)
474 raise _CheckoutError()
475
Mike Frysinger3538dd22019-08-26 15:32:06 -0400476 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700477 except _CheckoutError:
478 pass
479 except Exception as e:
480 print('error: Cannot checkout %s: %s: %s' %
481 (project.name, type(e).__name__, str(e)),
482 file=sys.stderr)
483 err_event.set()
484 raise
485 finally:
486 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800487 if not success:
488 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700489 lock.release()
490 finish = time.time()
491 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
492 start, finish, success)
493
494 return success
495
Mike Frysinger5a033082019-09-23 19:21:20 -0400496 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700497 """Checkout projects listed in all_projects
498
499 Args:
500 all_projects: List of all projects that should be checked out.
501 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400502 err_event: We'll set this event in the case of an error (after printing
503 out info about the error).
504 err_results: A list of strings, paths to git repos where checkout
505 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700506 """
507
508 # Perform checkouts in multiple threads when we are using partial clone.
509 # Without partial clone, all needed git objects are already downloaded,
510 # in this situation it's better to use only one process because the checkout
511 # would be mostly disk I/O; with partial clone, the objects are only
512 # downloaded when demanded (at checkout time), which is similar to the
513 # Sync_NetworkHalf case and parallelism would be helpful.
514 if self.manifest.CloneFilter:
515 syncjobs = self.jobs
516 else:
517 syncjobs = 1
518
519 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400520 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700521
522 threads = set()
523 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700524
525 for project in all_projects:
526 # Check for any errors before running any more tasks.
527 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400528 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700529 break
530
531 sem.acquire()
532 if project.worktree:
533 kwargs = dict(opt=opt,
534 sem=sem,
535 project=project,
536 lock=lock,
537 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800538 err_event=err_event,
539 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700540 if syncjobs > 1:
541 t = _threading.Thread(target=self._CheckoutWorker,
542 kwargs=kwargs)
543 # Ensure that Ctrl-C will not freeze the repo process.
544 t.daemon = True
545 threads.add(t)
546 t.start()
547 else:
548 self._CheckoutWorker(**kwargs)
549
550 for t in threads:
551 t.join()
552
553 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700554
Mike Frysinger5a033082019-09-23 19:21:20 -0400555 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700556 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700557 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500558 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500559 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900560 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500561 print('%s: Shared project %s found, disabling pruning.' %
562 (project.relpath, project.name))
563 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500564 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500565 else:
566 # This isn't perfect, but it's the best we can do with old git.
567 print('%s: WARNING: shared projects are unreliable when using old '
568 'versions of git; please upgrade to git-2.7.0+.'
569 % (project.relpath,),
570 file=sys.stderr)
571 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700572 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700573
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500574 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700575 cpu_count = multiprocessing.cpu_count()
576 else:
577 cpu_count = 1
578 jobs = min(self.jobs, cpu_count)
579
580 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700581 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700582 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700583 return
584
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400585 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700586
587 threads = set()
588 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700589
David James8d201162013-10-11 17:03:19 -0700590 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700591 try:
592 try:
David James8d201162013-10-11 17:03:19 -0700593 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700594 except GitError:
595 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900596 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700597 err_event.set()
598 raise
599 finally:
600 sem.release()
601
Gabe Black2ff30292014-10-09 17:54:35 -0700602 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400603 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700604 break
605 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700606 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700607 t.daemon = True
608 threads.add(t)
609 t.start()
610
611 for t in threads:
612 t.join()
613
Tim Kilbourn07669002013-03-08 15:02:49 -0800614 def _ReloadManifest(self, manifest_name=None):
615 if manifest_name:
616 # Override calls _Unload already
617 self.manifest.Override(manifest_name)
618 else:
619 self.manifest._Unload()
620
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500621 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700622 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700623 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700624 if project.relpath:
625 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700626 file_name = 'project.list'
627 file_path = os.path.join(self.manifest.repodir, file_name)
628 old_project_paths = []
629
630 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500631 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700632 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800633 # In reversed order, so subfolders are deleted before parent folder.
634 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700635 if not path:
636 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700637 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900638 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700639 gitdir = os.path.join(self.manifest.topdir, path, '.git')
640 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900641 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900642 manifest=self.manifest,
643 name=path,
644 remote=RemoteSpec('origin'),
645 gitdir=gitdir,
646 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500647 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900648 worktree=os.path.join(self.manifest.topdir, path),
649 relpath=path,
650 revisionExpr='HEAD',
651 revisionId=None,
652 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500653 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900654 quiet=opt.quiet,
655 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400656 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700657
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700658 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500659 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700660 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700661 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700662 return 0
663
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400664 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
665 if not self.manifest.manifest_server:
666 print('error: cannot smart sync: no manifest server defined in '
667 'manifest', file=sys.stderr)
668 sys.exit(1)
669
670 manifest_server = self.manifest.manifest_server
671 if not opt.quiet:
672 print('Using manifest server %s' % manifest_server)
673
David Pursehouseeeff3532020-02-12 11:24:10 +0900674 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400675 username = None
676 password = None
677 if opt.manifest_server_username and opt.manifest_server_password:
678 username = opt.manifest_server_username
679 password = opt.manifest_server_password
680 else:
681 try:
682 info = netrc.netrc()
683 except IOError:
684 # .netrc file does not exist or could not be opened
685 pass
686 else:
687 try:
688 parse_result = urllib.parse.urlparse(manifest_server)
689 if parse_result.hostname:
690 auth = info.authenticators(parse_result.hostname)
691 if auth:
692 username, _account, password = auth
693 else:
694 print('No credentials found for %s in .netrc'
695 % parse_result.hostname, file=sys.stderr)
696 except netrc.NetrcParseError as e:
697 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
698
699 if (username and password):
700 manifest_server = manifest_server.replace('://', '://%s:%s@' %
701 (username, password),
702 1)
703
704 transport = PersistentTransport(manifest_server)
705 if manifest_server.startswith('persistent-'):
706 manifest_server = manifest_server[len('persistent-'):]
707
708 try:
709 server = xmlrpc.client.Server(manifest_server, transport=transport)
710 if opt.smart_sync:
711 p = self.manifest.manifestProject
712 b = p.GetBranch(p.CurrentBranch)
713 branch = b.merge
714 if branch.startswith(R_HEADS):
715 branch = branch[len(R_HEADS):]
716
Mike Frysinger56ce3462019-12-04 19:30:48 -0500717 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500718 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400719 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500720 elif ('TARGET_PRODUCT' in os.environ and
721 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500722 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
723 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400724 [success, manifest_str] = server.GetApprovedManifest(branch, target)
725 else:
726 [success, manifest_str] = server.GetApprovedManifest(branch)
727 else:
728 assert(opt.smart_tag)
729 [success, manifest_str] = server.GetManifest(opt.smart_tag)
730
731 if success:
732 manifest_name = os.path.basename(smart_sync_manifest_path)
733 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500734 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400735 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400736 except IOError as e:
737 print('error: cannot write manifest to %s:\n%s'
738 % (smart_sync_manifest_path, e),
739 file=sys.stderr)
740 sys.exit(1)
741 self._ReloadManifest(manifest_name)
742 else:
743 print('error: manifest server RPC call failed: %s' %
744 manifest_str, file=sys.stderr)
745 sys.exit(1)
746 except (socket.error, IOError, xmlrpc.client.Fault) as e:
747 print('error: cannot connect to manifest server %s:\n%s'
748 % (self.manifest.manifest_server, e), file=sys.stderr)
749 sys.exit(1)
750 except xmlrpc.client.ProtocolError as e:
751 print('error: cannot connect to manifest server %s:\n%d %s'
752 % (self.manifest.manifest_server, e.errcode, e.errmsg),
753 file=sys.stderr)
754 sys.exit(1)
755
756 return manifest_name
757
Mike Frysingerfb527e32019-08-27 02:34:32 -0400758 def _UpdateManifestProject(self, opt, mp, manifest_name):
759 """Fetch & update the local manifest project."""
760 if not opt.local_only:
761 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500762 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400763 current_branch_only=opt.current_branch_only,
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200764 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500765 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400766 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600767 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400768 submodules=self.manifest.HasSubmodules,
769 clone_filter=self.manifest.CloneFilter)
770 finish = time.time()
771 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
772 start, finish, success)
773
774 if mp.HasChanges:
775 syncbuf = SyncBuffer(mp.config)
776 start = time.time()
777 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
778 clean = syncbuf.Finish()
779 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
780 start, time.time(), clean)
781 if not clean:
782 sys.exit(1)
783 self._ReloadManifest(opt.manifest_name)
784 if opt.jobs is None:
785 self.jobs = self.manifest.default.sync_j
786
Mike Frysingerae6cb082019-08-27 01:10:59 -0400787 def ValidateOptions(self, opt, args):
788 if opt.force_broken:
789 print('warning: -f/--force-broken is now the default behavior, and the '
790 'options are deprecated', file=sys.stderr)
791 if opt.network_only and opt.detach_head:
792 self.OptionParser.error('cannot combine -n and -d')
793 if opt.network_only and opt.local_only:
794 self.OptionParser.error('cannot combine -n and -l')
795 if opt.manifest_name and opt.smart_sync:
796 self.OptionParser.error('cannot combine -m and -s')
797 if opt.manifest_name and opt.smart_tag:
798 self.OptionParser.error('cannot combine -m and -t')
799 if opt.manifest_server_username or opt.manifest_server_password:
800 if not (opt.smart_sync or opt.smart_tag):
801 self.OptionParser.error('-u and -p may only be combined with -s or -t')
802 if None in [opt.manifest_server_username, opt.manifest_server_password]:
803 self.OptionParser.error('both -u and -p must be given')
804
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700805 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800806 if opt.jobs:
807 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700808 if self.jobs > 1:
809 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400810 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700811
Mike Frysinger521d01b2020-02-17 01:51:49 -0500812 opt.quiet = opt.output_mode is False
813 opt.verbose = opt.output_mode is True
814
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500815 if opt.manifest_name:
816 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700817
Chirayu Desaia892b102013-06-11 14:18:46 +0530818 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900819 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900820 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530821
Xin Lid79a4bc2020-05-20 16:03:45 -0700822 if opt.clone_bundle is None:
823 opt.clone_bundle = self.manifest.CloneBundle
824
Victor Boivie08c880d2011-04-19 10:32:52 +0200825 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400826 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
827 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900828 if os.path.isfile(smart_sync_manifest_path):
829 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800830 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900831 except OSError as e:
832 print('error: failed to remove existing smart sync override manifest: %s' %
833 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700834
Mike Frysinger5a033082019-09-23 19:21:20 -0400835 err_event = _threading.Event()
836
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700837 rp = self.manifest.repoProject
838 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500839 cb = rp.CurrentBranch
840 if cb:
841 base = rp.GetBranch(cb).merge
842 if not base or not base.startswith('refs/heads/'):
843 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400844 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500845 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700846
847 mp = self.manifest.manifestProject
848 mp.PreSync()
849
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800850 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700851 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800852
Fredrik de Grootcc960972019-11-22 09:04:31 +0100853 if not opt.mp_update:
854 print('Skipping update of local manifest project.')
855 else:
856 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700857
Simran Basib9a1b732015-08-20 12:19:28 -0700858 if self.gitc_manifest:
859 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700860 missing_ok=True)
861 gitc_projects = []
862 opened_projects = []
863 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700864 if project.relpath in self.gitc_manifest.paths and \
865 self.gitc_manifest.paths[project.relpath].old_revision:
866 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700867 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700868 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700869
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700870 if not args:
871 gitc_projects = None
872
873 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700874 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700875 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
876 if manifest_name:
877 manifest.Override(manifest_name)
878 else:
879 manifest.Override(self.manifest.manifestFile)
880 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
881 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700882 gitc_projects)
883 print('GITC client successfully synced.')
884
885 # The opened projects need to be synced as normal, therefore we
886 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700887 # TODO: make this more reliable -- if there's a project name/path overlap,
888 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900889 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
890 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700891 if not args:
892 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800893 all_projects = self.GetProjects(args,
894 missing_ok=True,
895 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700896
Mike Frysinger5a033082019-09-23 19:21:20 -0400897 err_network_sync = False
898 err_update_projects = False
899 err_checkout = False
900
Dave Borowitz67700e92012-10-23 15:00:54 -0700901 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700902 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700903 to_fetch = []
904 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700905 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700906 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900907 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700908 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700909
Mike Frysinger5a033082019-09-23 19:21:20 -0400910 fetched = self._Fetch(to_fetch, opt, err_event)
911
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500912 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700913 if opt.network_only:
914 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400915 if err_event.isSet():
916 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
917 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700918 return
919
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800920 # Iteratively fetch missing and/or nested unregistered submodules
921 previously_missing_set = set()
922 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100923 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800924 all_projects = self.GetProjects(args,
925 missing_ok=True,
926 submodules_ok=opt.fetch_submodules)
927 missing = []
928 for project in all_projects:
929 if project.gitdir not in fetched:
930 missing.append(project)
931 if not missing:
932 break
933 # Stop us from non-stopped fetching actually-missing repos: If set of
934 # missing repos has not been changed from last fetch, we break.
935 missing_set = set(p.name for p in missing)
936 if previously_missing_set == missing_set:
937 break
938 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400939 fetched.update(self._Fetch(missing, opt, err_event))
940
941 # If we saw an error, exit with code 1 so that other scripts can check.
942 if err_event.isSet():
943 err_network_sync = True
944 if opt.fail_fast:
945 print('\nerror: Exited sync due to fetch errors.\n'
946 'Local checkouts *not* updated. Resolve network issues & '
947 'retry.\n'
948 '`repo sync -l` will update some local checkouts.',
949 file=sys.stderr)
950 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800951
Julien Campergue335f5ef2013-10-16 11:02:35 +0200952 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700953 # bail out now, we have no working tree
954 return
955
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500956 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400957 err_event.set()
958 err_update_projects = True
959 if opt.fail_fast:
960 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
961 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700962
Mike Frysinger5a033082019-09-23 19:21:20 -0400963 err_results = []
964 self._Checkout(all_projects, opt, err_event, err_results)
965 if err_event.isSet():
966 err_checkout = True
967 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700968
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700969 # If there's a notice that's supposed to print at the end of the sync, print
970 # it now...
971 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700972 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700973
Mike Frysinger5a033082019-09-23 19:21:20 -0400974 # If we saw an error, exit with code 1 so that other scripts can check.
975 if err_event.isSet():
976 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
977 if err_network_sync:
978 print('error: Downloading network changes failed.', file=sys.stderr)
979 if err_update_projects:
980 print('error: Updating local project lists failed.', file=sys.stderr)
981 if err_checkout:
982 print('error: Checking out local projects failed.', file=sys.stderr)
983 if err_results:
984 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
985 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
986 file=sys.stderr)
987 sys.exit(1)
988
Mike Frysingere19d9e12020-02-12 11:23:32 -0500989 if not opt.quiet:
990 print('repo sync has finished successfully.')
991
David Pursehouse819827a2020-02-12 15:20:19 +0900992
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700993def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800994 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700995 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700996 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800997 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700998 if project.Exists:
999 project.PostRepoUpgrade()
1000
David Pursehouse819827a2020-02-12 15:20:19 +09001001
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001002def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001003 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001004 print('info: A new version of repo is available', file=sys.stderr)
1005 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001006 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001007 syncbuf = SyncBuffer(rp.config)
1008 rp.Sync_LocalHalf(syncbuf)
1009 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001010 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001011 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001012 raise RepoChangedException(['--repo-upgraded'])
1013 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001014 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001015 else:
1016 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001017 print('repo version %s is current' % rp.work_git.describe(HEAD),
1018 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001019
David Pursehouse819827a2020-02-12 15:20:19 +09001020
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001021def _VerifyTag(project):
1022 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1023 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001024 print('warning: GnuPG was not available during last "repo init"\n'
1025 'warning: Cannot automatically authenticate repo."""',
1026 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001027 return True
1028
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001029 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001030 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001031 except GitError:
1032 cur = None
1033
1034 if not cur \
1035 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001036 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001037 if rev.startswith(R_HEADS):
1038 rev = rev[len(R_HEADS):]
1039
Sarah Owenscecd1d82012-11-01 22:59:27 -07001040 print(file=sys.stderr)
1041 print("warning: project '%s' branch '%s' is not signed"
1042 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001043 return False
1044
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001045 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001046 env['GIT_DIR'] = project.gitdir
1047 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001048
1049 cmd = [GIT, 'tag', '-v', cur]
1050 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001051 stdout=subprocess.PIPE,
1052 stderr=subprocess.PIPE,
1053 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001054 out = proc.stdout.read()
1055 proc.stdout.close()
1056
1057 err = proc.stderr.read()
1058 proc.stderr.close()
1059
1060 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001061 print(file=sys.stderr)
1062 print(out, file=sys.stderr)
1063 print(err, file=sys.stderr)
1064 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065 return False
1066 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001067
David Rileye0684ad2017-04-05 00:02:59 -07001068
Dave Borowitz67700e92012-10-23 15:00:54 -07001069class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001070 _ALPHA = 0.5
1071
Dave Borowitz67700e92012-10-23 15:00:54 -07001072 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001073 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001074 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001075 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001076
1077 def Get(self, project):
1078 self._Load()
1079 return self._times.get(project.name, _ONE_DAY_S)
1080
1081 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001082 self._Load()
1083 name = project.name
1084 old = self._times.get(name, t)
1085 self._seen.add(name)
1086 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001087 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001088
1089 def _Load(self):
1090 if self._times is None:
1091 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001092 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001093 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001094 except (IOError, ValueError):
1095 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001096 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001097 except OSError:
1098 pass
1099 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001100
1101 def Save(self):
1102 if self._times is None:
1103 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001104
1105 to_delete = []
1106 for name in self._times:
1107 if name not in self._seen:
1108 to_delete.append(name)
1109 for name in to_delete:
1110 del self._times[name]
1111
Dave Borowitz67700e92012-10-23 15:00:54 -07001112 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001113 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001114 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001115 except (IOError, TypeError):
1116 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001117 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001118 except OSError:
1119 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001120
1121# This is a replacement for xmlrpc.client.Transport using urllib2
1122# and supporting persistent-http[s]. It cannot change hosts from
1123# request to request like the normal transport, the real url
1124# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001125
1126
Dan Willemsen0745bb22015-08-17 13:41:45 -07001127class PersistentTransport(xmlrpc.client.Transport):
1128 def __init__(self, orig_host):
1129 self.orig_host = orig_host
1130
1131 def request(self, host, handler, request_body, verbose=False):
1132 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1133 # Python doesn't understand cookies with the #HttpOnly_ prefix
1134 # Since we're only using them for HTTP, copy the file temporarily,
1135 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001136 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001137 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001138 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001139 try:
1140 with open(cookiefile) as f:
1141 for line in f:
1142 if line.startswith("#HttpOnly_"):
1143 line = line[len("#HttpOnly_"):]
1144 tmpcookiefile.write(line)
1145 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001146
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001147 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001148 try:
1149 cookiejar.load()
1150 except cookielib.LoadError:
1151 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001152 finally:
1153 tmpcookiefile.close()
1154 else:
1155 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001156
1157 proxyhandler = urllib.request.ProxyHandler
1158 if proxy:
1159 proxyhandler = urllib.request.ProxyHandler({
1160 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001161 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001162
1163 opener = urllib.request.build_opener(
1164 urllib.request.HTTPCookieProcessor(cookiejar),
1165 proxyhandler)
1166
1167 url = urllib.parse.urljoin(self.orig_host, handler)
1168 parse_results = urllib.parse.urlparse(url)
1169
1170 scheme = parse_results.scheme
1171 if scheme == 'persistent-http':
1172 scheme = 'http'
1173 if scheme == 'persistent-https':
1174 # If we're proxying through persistent-https, use http. The
1175 # proxy itself will do the https.
1176 if proxy:
1177 scheme = 'http'
1178 else:
1179 scheme = 'https'
1180
1181 # Parse out any authentication information using the base class
1182 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1183
1184 url = urllib.parse.urlunparse((
1185 scheme,
1186 host,
1187 parse_results.path,
1188 parse_results.params,
1189 parse_results.query,
1190 parse_results.fragment))
1191
1192 request = urllib.request.Request(url, request_body)
1193 if extra_headers is not None:
1194 for (name, header) in extra_headers:
1195 request.add_header(name, header)
1196 request.add_header('Content-Type', 'text/xml')
1197 try:
1198 response = opener.open(request)
1199 except urllib.error.HTTPError as e:
1200 if e.code == 501:
1201 # We may have been redirected through a login process
1202 # but our POST turned into a GET. Retry.
1203 response = opener.open(request)
1204 else:
1205 raise
1206
1207 p, u = xmlrpc.client.getparser()
1208 while 1:
1209 data = response.read(1024)
1210 if not data:
1211 break
1212 p.feed(data)
1213 p.close()
1214 return u.close()
1215
1216 def close(self):
1217 pass