blob: d6b8f9dc6e2513a22f66fe6459c974fb3bc039b8 [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
Raman Tenneti6a872c92021-01-14 19:17:50 -080054import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070055import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070056from project import Project
57from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080058from command import Command, MirrorSafeCommand
Raman Tenneti6a872c92021-01-14 19:17:50 -080059from error import BUG_REPORT_URL, RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070060import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070061from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070062from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080063from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070064from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070065
Dave Borowitz67700e92012-10-23 15:00:54 -070066_ONE_DAY_S = 24 * 60 * 60
67
David Pursehouse819827a2020-02-12 15:20:19 +090068
Doug Andersonfc06ced2011-03-16 15:49:18 -070069class _FetchError(Exception):
70 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
71 pass
72
David Pursehouse819827a2020-02-12 15:20:19 +090073
Xin Li745be2e2019-06-03 11:24:30 -070074class _CheckoutError(Exception):
75 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
76
David Pursehouse819827a2020-02-12 15:20:19 +090077
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080078class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080079 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070080 common = True
81 helpSummary = "Update working tree to the latest revision"
82 helpUsage = """
83%prog [<project>...]
84"""
85 helpDescription = """
86The '%prog' command synchronizes local project directories
87with the remote repositories specified in the manifest. If a local
88project does not yet exist, it will clone a new local directory from
89the remote repository and set up tracking branches as specified in
90the manifest. If the local project already exists, '%prog'
91will update the remote branches and rebase any new local changes
92on top of the new remote changes.
93
94'%prog' will synchronize all projects listed at the command
95line. Projects can be specified either by name, or by a relative
96or absolute path to the project's local directory. If no projects
97are specified, '%prog' will synchronize all projects listed in
98the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070099
100The -d/--detach option can be used to switch specified projects
101back to the manifest revision. This option is especially helpful
102if the project is currently on a topic branch, but the manifest
103revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700104
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700105The -s/--smart-sync option can be used to sync to a known good
106build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200107manifest. The -t/--smart-tag option is similar and allows you to
108specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700109
David Pursehousecf76b1b2012-09-14 10:31:42 +0900110The -u/--manifest-server-username and -p/--manifest-server-password
111options can be used to specify a username and password to authenticate
112with the manifest server when using the -s or -t option.
113
114If -u and -p are not specified when using the -s or -t option, '%prog'
115will attempt to read authentication credentials for the manifest server
116from the user's .netrc file.
117
118'%prog' will not use authentication credentials from -u/-p or .netrc
119if the manifest server specified in the manifest file already includes
120credentials.
121
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400122By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400123to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500124
Kevin Degiabaa7f32014-11-12 11:27:45 -0700125The --force-sync option can be used to overwrite existing git
126directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900127object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700128refs may be removed when overwriting.
129
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500130The --force-remove-dirty option can be used to remove previously used
131projects with uncommitted changes. WARNING: This may cause data to be
132lost since uncommitted changes may be removed with projects that no longer
133exist in the manifest.
134
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700135The --no-clone-bundle option disables any attempt to use
136$URL/clone.bundle to bootstrap a new Git repository from a
137resumeable bundle file on a content delivery network. This
138may be necessary if there are problems with the local Python
139HTTP client or proxy configuration, but the Git binary works.
140
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800141The --fetch-submodules option enables fetching Git submodules
142of a project from server.
143
David Pursehousef2fad612015-01-29 14:36:28 +0900144The -c/--current-branch option can be used to only fetch objects that
145are on the branch specified by a project's revision.
146
David Pursehouseb1553542014-09-04 21:28:09 +0900147The --optimized-fetch option can be used to only fetch projects that
148are fixed to a sha1 revision if the sha1 revision does not already
149exist locally.
150
David Pursehouse74cfd272015-10-14 10:50:15 +0900151The --prune option can be used to remove any refs that no longer
152exist on the remote.
153
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400154# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700155
156If at least one project remote URL uses an SSH connection (ssh://,
157git+ssh://, or user@host:path syntax) repo will automatically
158enable the SSH ControlMaster option when connecting to that host.
159This feature permits other projects in the same '%prog' session to
160reuse the same SSH tunnel, saving connection setup overheads.
161
162To disable this behavior on UNIX platforms, set the GIT_SSH
163environment variable to 'ssh'. For example:
164
165 export GIT_SSH=ssh
166 %prog
167
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400168# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700169
170This feature is automatically disabled on Windows, due to the lack
171of UNIX domain socket support.
172
173This feature is not compatible with url.insteadof rewrites in the
174user's ~/.gitconfig. '%prog' is currently not able to perform the
175rewrite early enough to establish the ControlMaster tunnel.
176
177If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
178later is required to fix a server side protocol bug.
179
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700180"""
181
Nico Sallembien6623b212010-05-11 12:57:01 -0700182 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000183 try:
184 self.jobs = self.manifest.default.sync_j
185 except ManifestParseError:
186 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700187
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500188 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200189 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400190 help='obsolete option (to be deleted in the future)')
191 p.add_option('--fail-fast',
192 dest='fail_fast', action='store_true',
193 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700194 p.add_option('--force-sync',
195 dest='force_sync', action='store_true',
196 help="overwrite an existing git directory if it needs to "
197 "point to a different object directory. WARNING: this "
198 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500199 p.add_option('--force-remove-dirty',
200 dest='force_remove_dirty', action='store_true',
201 help="force remove projects with uncommitted modifications if "
202 "projects no longer exist in the manifest. "
203 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700205 dest='local_only', action='store_true',
206 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900207 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100208 dest='mp_update', action='store_false', default='true',
209 help='use the existing manifest checkout as-is. '
210 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700212 dest='network_only', action='store_true',
213 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700215 dest='detach_head', action='store_true',
216 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700218 dest='current_branch_only', action='store_true',
219 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500220 p.add_option('-v', '--verbose',
221 dest='output_mode', action='store_true',
222 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900223 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500224 dest='output_mode', action='store_false',
225 help='only show errors')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900226 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800227 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700228 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500229 p.add_option('-m', '--manifest-name',
230 dest='manifest_name',
231 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700232 p.add_option('--clone-bundle', action='store_true',
233 help='enable use of /clone.bundle on HTTP/HTTPS')
234 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700235 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800236 p.add_option('-u', '--manifest-server-username', action='store',
237 dest='manifest_server_username',
238 help='username to authenticate with the manifest server')
239 p.add_option('-p', '--manifest-server-password', action='store',
240 dest='manifest_server_password',
241 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800242 p.add_option('--fetch-submodules',
243 dest='fetch_submodules', action='store_true',
244 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800245 p.add_option('--use-superproject', action='store_true',
246 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700247 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500248 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700249 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900250 p.add_option('--optimized-fetch',
251 dest='optimized_fetch', action='store_true',
252 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600253 p.add_option('--retry-fetches',
254 default=0, action='store', type='int',
255 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900256 p.add_option('--prune', dest='prune', action='store_true',
257 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700258 if show_smart:
259 p.add_option('-s', '--smart-sync',
260 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900261 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200262 p.add_option('-t', '--smart-tag',
263 dest='smart_tag', action='store',
264 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700265
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700266 g = p.add_option_group('repo Version options')
267 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500268 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700269 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700270 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800271 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700272 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700273
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500274 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700275 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800276
David James8d201162013-10-11 17:03:19 -0700277 Delegates most of the work to _FetchHelper.
278
279 Args:
280 opt: Program options returned from optparse. See _Options().
281 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500282 sem: We'll release() this semaphore when we exit so that another thread
283 can be started up.
David James89ece422014-01-09 18:51:58 -0800284 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700285 _FetchHelper docstring for details.
286 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500287 try:
288 for project in projects:
289 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400290 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500291 break
292 finally:
293 sem.release()
David James8d201162013-10-11 17:03:19 -0700294
Xin Li745be2e2019-06-03 11:24:30 -0700295 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
296 clone_filter):
David James8d201162013-10-11 17:03:19 -0700297 """Fetch git objects for a single project.
298
David Pursehousec1b86a22012-11-14 11:36:51 +0900299 Args:
300 opt: Program options returned from optparse. See _Options().
301 project: Project object for the project to fetch.
302 lock: Lock for accessing objects that are shared amongst multiple
303 _FetchHelper() threads.
304 fetched: set object that we will add project.gitdir to when we're done
305 (with our lock held).
306 pm: Instance of a Project object. We will call pm.update() (with our
307 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900308 err_event: We'll set this event in the case of an error (after printing
309 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700310 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700311
312 Returns:
313 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900314 """
315 # We'll set to true once we've locked the lock.
316 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700317
David Pursehousec1b86a22012-11-14 11:36:51 +0900318 # Encapsulate everything in a try/except/finally so that:
319 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900320 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700321 start = time.time()
322 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900323 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700324 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900325 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900326 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500327 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900328 current_branch_only=opt.current_branch_only,
329 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500330 clone_bundle=opt.clone_bundle,
331 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900332 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600333 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900334 prune=opt.prune,
335 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900336 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700337
David Pursehousec1b86a22012-11-14 11:36:51 +0900338 # Lock around all the rest of the code, since printing, updating a set
339 # and Progress.update() are not thread safe.
340 lock.acquire()
341 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700342
David Pursehousec1b86a22012-11-14 11:36:51 +0900343 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800344 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700345 print('error: Cannot fetch %s from %s'
346 % (project.name, project.remote.url),
347 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400348 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900349 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700350
David Pursehousec1b86a22012-11-14 11:36:51 +0900351 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400352 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900353 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800354 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400355 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900356 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900357 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900358 err_event.set()
359 raise
360 finally:
361 if did_lock:
362 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700363 finish = time.time()
364 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
365 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800366
David James8d201162013-10-11 17:03:19 -0700367 return success
368
Mike Frysinger5a033082019-09-23 19:21:20 -0400369 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700370 fetched = set()
David James89ece422014-01-09 18:51:58 -0800371 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200372 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200373 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800374
David James89ece422014-01-09 18:51:58 -0800375 objdir_project_map = dict()
376 for project in projects:
377 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700378
David James89ece422014-01-09 18:51:58 -0800379 threads = set()
380 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800381 for project_list in objdir_project_map.values():
382 # Check for any errors before running any more tasks.
383 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400384 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800385 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700386
David James89ece422014-01-09 18:51:58 -0800387 sem.acquire()
388 kwargs = dict(opt=opt,
389 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500390 sem=sem,
David James89ece422014-01-09 18:51:58 -0800391 lock=lock,
392 fetched=fetched,
393 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700394 err_event=err_event,
395 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800396 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900397 t = _threading.Thread(target=self._FetchProjectList,
398 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200399 # Ensure that Ctrl-C will not freeze the repo process.
400 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800401 threads.add(t)
402 t.start()
David James89ece422014-01-09 18:51:58 -0800403 else:
404 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800405
David James89ece422014-01-09 18:51:58 -0800406 for t in threads:
407 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800408
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700409 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700410 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700411
Julien Campergue335f5ef2013-10-16 11:02:35 +0200412 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400413 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200414
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700415 return fetched
416
Xin Li745be2e2019-06-03 11:24:30 -0700417 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
418 """Main function of the fetch threads.
419
420 Delegates most of the work to _CheckoutOne.
421
422 Args:
423 opt: Program options returned from optparse. See _Options().
424 projects: Projects to fetch.
425 sem: We'll release() this semaphore when we exit so that another thread
426 can be started up.
427 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
428 _CheckoutOne docstring for details.
429 """
430 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400431 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700432 finally:
433 sem.release()
434
Vadim Bendeburydff91942019-11-06 11:05:00 -0800435 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700436 """Checkout work tree for one project
437
438 Args:
439 opt: Program options returned from optparse. See _Options().
440 project: Project object for the project to checkout.
441 lock: Lock for accessing objects that are shared amongst multiple
442 _CheckoutWorker() threads.
443 pm: Instance of a Project object. We will call pm.update() (with our
444 lock held).
445 err_event: We'll set this event in the case of an error (after printing
446 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800447 err_results: A list of strings, paths to git repos where checkout
448 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700449
450 Returns:
451 Whether the fetch was successful.
452 """
453 # We'll set to true once we've locked the lock.
454 did_lock = False
455
Xin Li745be2e2019-06-03 11:24:30 -0700456 # Encapsulate everything in a try/except/finally so that:
457 # - We always set err_event in the case of an exception.
458 # - We always make sure we unlock the lock if we locked it.
459 start = time.time()
460 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
461 detach_head=opt.detach_head)
462 success = False
463 try:
464 try:
465 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700466
467 # Lock around all the rest of the code, since printing, updating a set
468 # and Progress.update() are not thread safe.
469 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400470 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700471 did_lock = True
472
473 if not success:
474 err_event.set()
475 print('error: Cannot checkout %s' % (project.name),
476 file=sys.stderr)
477 raise _CheckoutError()
478
Mike Frysinger3538dd22019-08-26 15:32:06 -0400479 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700480 except _CheckoutError:
481 pass
482 except Exception as e:
483 print('error: Cannot checkout %s: %s: %s' %
484 (project.name, type(e).__name__, str(e)),
485 file=sys.stderr)
486 err_event.set()
487 raise
488 finally:
489 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800490 if not success:
491 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700492 lock.release()
493 finish = time.time()
494 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
495 start, finish, success)
496
497 return success
498
Mike Frysinger5a033082019-09-23 19:21:20 -0400499 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700500 """Checkout projects listed in all_projects
501
502 Args:
503 all_projects: List of all projects that should be checked out.
504 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400505 err_event: We'll set this event in the case of an error (after printing
506 out info about the error).
507 err_results: A list of strings, paths to git repos where checkout
508 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700509 """
510
511 # Perform checkouts in multiple threads when we are using partial clone.
512 # Without partial clone, all needed git objects are already downloaded,
513 # in this situation it's better to use only one process because the checkout
514 # would be mostly disk I/O; with partial clone, the objects are only
515 # downloaded when demanded (at checkout time), which is similar to the
516 # Sync_NetworkHalf case and parallelism would be helpful.
517 if self.manifest.CloneFilter:
518 syncjobs = self.jobs
519 else:
520 syncjobs = 1
521
522 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400523 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700524
525 threads = set()
526 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700527
528 for project in all_projects:
529 # Check for any errors before running any more tasks.
530 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400531 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700532 break
533
534 sem.acquire()
535 if project.worktree:
536 kwargs = dict(opt=opt,
537 sem=sem,
538 project=project,
539 lock=lock,
540 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800541 err_event=err_event,
542 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700543 if syncjobs > 1:
544 t = _threading.Thread(target=self._CheckoutWorker,
545 kwargs=kwargs)
546 # Ensure that Ctrl-C will not freeze the repo process.
547 t.daemon = True
548 threads.add(t)
549 t.start()
550 else:
551 self._CheckoutWorker(**kwargs)
552
553 for t in threads:
554 t.join()
555
556 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700557
Mike Frysinger5a033082019-09-23 19:21:20 -0400558 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700559 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700560 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500561 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500562 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900563 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500564 print('%s: Shared project %s found, disabling pruning.' %
565 (project.relpath, project.name))
566 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500567 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500568 else:
569 # This isn't perfect, but it's the best we can do with old git.
570 print('%s: WARNING: shared projects are unreliable when using old '
571 'versions of git; please upgrade to git-2.7.0+.'
572 % (project.relpath,),
573 file=sys.stderr)
574 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700575 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700576
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500577 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700578 cpu_count = multiprocessing.cpu_count()
579 else:
580 cpu_count = 1
581 jobs = min(self.jobs, cpu_count)
582
583 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700584 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700585 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700586 return
587
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400588 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700589
590 threads = set()
591 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700592
David James8d201162013-10-11 17:03:19 -0700593 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700594 try:
595 try:
David James8d201162013-10-11 17:03:19 -0700596 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700597 except GitError:
598 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900599 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700600 err_event.set()
601 raise
602 finally:
603 sem.release()
604
Gabe Black2ff30292014-10-09 17:54:35 -0700605 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400606 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700607 break
608 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700609 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700610 t.daemon = True
611 threads.add(t)
612 t.start()
613
614 for t in threads:
615 t.join()
616
Tim Kilbourn07669002013-03-08 15:02:49 -0800617 def _ReloadManifest(self, manifest_name=None):
618 if manifest_name:
619 # Override calls _Unload already
620 self.manifest.Override(manifest_name)
621 else:
622 self.manifest._Unload()
623
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500624 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700625 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700626 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700627 if project.relpath:
628 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700629 file_name = 'project.list'
630 file_path = os.path.join(self.manifest.repodir, file_name)
631 old_project_paths = []
632
633 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500634 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700635 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800636 # In reversed order, so subfolders are deleted before parent folder.
637 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700638 if not path:
639 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700640 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900641 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700642 gitdir = os.path.join(self.manifest.topdir, path, '.git')
643 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900644 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900645 manifest=self.manifest,
646 name=path,
647 remote=RemoteSpec('origin'),
648 gitdir=gitdir,
649 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500650 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900651 worktree=os.path.join(self.manifest.topdir, path),
652 relpath=path,
653 revisionExpr='HEAD',
654 revisionId=None,
655 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500656 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900657 quiet=opt.quiet,
658 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400659 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700660
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700661 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500662 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700663 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700664 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700665 return 0
666
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400667 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
668 if not self.manifest.manifest_server:
669 print('error: cannot smart sync: no manifest server defined in '
670 'manifest', file=sys.stderr)
671 sys.exit(1)
672
673 manifest_server = self.manifest.manifest_server
674 if not opt.quiet:
675 print('Using manifest server %s' % manifest_server)
676
David Pursehouseeeff3532020-02-12 11:24:10 +0900677 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400678 username = None
679 password = None
680 if opt.manifest_server_username and opt.manifest_server_password:
681 username = opt.manifest_server_username
682 password = opt.manifest_server_password
683 else:
684 try:
685 info = netrc.netrc()
686 except IOError:
687 # .netrc file does not exist or could not be opened
688 pass
689 else:
690 try:
691 parse_result = urllib.parse.urlparse(manifest_server)
692 if parse_result.hostname:
693 auth = info.authenticators(parse_result.hostname)
694 if auth:
695 username, _account, password = auth
696 else:
697 print('No credentials found for %s in .netrc'
698 % parse_result.hostname, file=sys.stderr)
699 except netrc.NetrcParseError as e:
700 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
701
702 if (username and password):
703 manifest_server = manifest_server.replace('://', '://%s:%s@' %
704 (username, password),
705 1)
706
707 transport = PersistentTransport(manifest_server)
708 if manifest_server.startswith('persistent-'):
709 manifest_server = manifest_server[len('persistent-'):]
710
711 try:
712 server = xmlrpc.client.Server(manifest_server, transport=transport)
713 if opt.smart_sync:
714 p = self.manifest.manifestProject
715 b = p.GetBranch(p.CurrentBranch)
716 branch = b.merge
717 if branch.startswith(R_HEADS):
718 branch = branch[len(R_HEADS):]
719
Mike Frysinger56ce3462019-12-04 19:30:48 -0500720 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500721 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400722 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500723 elif ('TARGET_PRODUCT' in os.environ and
724 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500725 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
726 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400727 [success, manifest_str] = server.GetApprovedManifest(branch, target)
728 else:
729 [success, manifest_str] = server.GetApprovedManifest(branch)
730 else:
731 assert(opt.smart_tag)
732 [success, manifest_str] = server.GetManifest(opt.smart_tag)
733
734 if success:
735 manifest_name = os.path.basename(smart_sync_manifest_path)
736 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500737 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400738 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400739 except IOError as e:
740 print('error: cannot write manifest to %s:\n%s'
741 % (smart_sync_manifest_path, e),
742 file=sys.stderr)
743 sys.exit(1)
744 self._ReloadManifest(manifest_name)
745 else:
746 print('error: manifest server RPC call failed: %s' %
747 manifest_str, file=sys.stderr)
748 sys.exit(1)
749 except (socket.error, IOError, xmlrpc.client.Fault) as e:
750 print('error: cannot connect to manifest server %s:\n%s'
751 % (self.manifest.manifest_server, e), file=sys.stderr)
752 sys.exit(1)
753 except xmlrpc.client.ProtocolError as e:
754 print('error: cannot connect to manifest server %s:\n%d %s'
755 % (self.manifest.manifest_server, e.errcode, e.errmsg),
756 file=sys.stderr)
757 sys.exit(1)
758
759 return manifest_name
760
Mike Frysingerfb527e32019-08-27 02:34:32 -0400761 def _UpdateManifestProject(self, opt, mp, manifest_name):
762 """Fetch & update the local manifest project."""
763 if not opt.local_only:
764 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500765 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400766 current_branch_only=opt.current_branch_only,
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200767 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500768 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400769 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600770 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400771 submodules=self.manifest.HasSubmodules,
772 clone_filter=self.manifest.CloneFilter)
773 finish = time.time()
774 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
775 start, finish, success)
776
777 if mp.HasChanges:
778 syncbuf = SyncBuffer(mp.config)
779 start = time.time()
780 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
781 clean = syncbuf.Finish()
782 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
783 start, time.time(), clean)
784 if not clean:
785 sys.exit(1)
786 self._ReloadManifest(opt.manifest_name)
787 if opt.jobs is None:
788 self.jobs = self.manifest.default.sync_j
789
Mike Frysingerae6cb082019-08-27 01:10:59 -0400790 def ValidateOptions(self, opt, args):
791 if opt.force_broken:
792 print('warning: -f/--force-broken is now the default behavior, and the '
793 'options are deprecated', file=sys.stderr)
794 if opt.network_only and opt.detach_head:
795 self.OptionParser.error('cannot combine -n and -d')
796 if opt.network_only and opt.local_only:
797 self.OptionParser.error('cannot combine -n and -l')
798 if opt.manifest_name and opt.smart_sync:
799 self.OptionParser.error('cannot combine -m and -s')
800 if opt.manifest_name and opt.smart_tag:
801 self.OptionParser.error('cannot combine -m and -t')
802 if opt.manifest_server_username or opt.manifest_server_password:
803 if not (opt.smart_sync or opt.smart_tag):
804 self.OptionParser.error('-u and -p may only be combined with -s or -t')
805 if None in [opt.manifest_server_username, opt.manifest_server_password]:
806 self.OptionParser.error('both -u and -p must be given')
807
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700808 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800809 if opt.jobs:
810 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700811 if self.jobs > 1:
812 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400813 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700814
Mike Frysinger521d01b2020-02-17 01:51:49 -0500815 opt.quiet = opt.output_mode is False
816 opt.verbose = opt.output_mode is True
817
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500818 if opt.manifest_name:
819 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700820
Chirayu Desaia892b102013-06-11 14:18:46 +0530821 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900822 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900823 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530824
Xin Lid79a4bc2020-05-20 16:03:45 -0700825 if opt.clone_bundle is None:
826 opt.clone_bundle = self.manifest.CloneBundle
827
Victor Boivie08c880d2011-04-19 10:32:52 +0200828 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400829 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
830 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900831 if os.path.isfile(smart_sync_manifest_path):
832 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800833 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900834 except OSError as e:
835 print('error: failed to remove existing smart sync override manifest: %s' %
836 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700837
Mike Frysinger5a033082019-09-23 19:21:20 -0400838 err_event = _threading.Event()
839
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700840 rp = self.manifest.repoProject
841 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500842 cb = rp.CurrentBranch
843 if cb:
844 base = rp.GetBranch(cb).merge
845 if not base or not base.startswith('refs/heads/'):
846 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400847 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500848 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700849
850 mp = self.manifest.manifestProject
851 mp.PreSync()
852
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800853 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700854 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800855
Fredrik de Grootcc960972019-11-22 09:04:31 +0100856 if not opt.mp_update:
857 print('Skipping update of local manifest project.')
858 else:
859 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700860
Simran Basib9a1b732015-08-20 12:19:28 -0700861 if self.gitc_manifest:
862 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700863 missing_ok=True)
864 gitc_projects = []
865 opened_projects = []
866 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700867 if project.relpath in self.gitc_manifest.paths and \
868 self.gitc_manifest.paths[project.relpath].old_revision:
869 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700870 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700871 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700872
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700873 if not args:
874 gitc_projects = None
875
876 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700877 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700878 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
879 if manifest_name:
880 manifest.Override(manifest_name)
881 else:
882 manifest.Override(self.manifest.manifestFile)
883 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
884 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700885 gitc_projects)
886 print('GITC client successfully synced.')
887
888 # The opened projects need to be synced as normal, therefore we
889 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700890 # TODO: make this more reliable -- if there's a project name/path overlap,
891 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900892 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
893 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700894 if not args:
895 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800896 all_projects = self.GetProjects(args,
897 missing_ok=True,
898 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700899
Raman Tenneti6a872c92021-01-14 19:17:50 -0800900 if opt.use_superproject:
901 if not self.manifest.superproject:
902 print('error: superproject tag is not defined in manifest.xml',
903 file=sys.stderr)
904 sys.exit(1)
905 print('WARNING: --use-superproject is experimental and not '
906 'for general use', file=sys.stderr)
907 superproject_url = self.manifest.superproject['remote'].url
908 if not superproject_url:
909 print('error: superproject URL is not defined in manifest.xml',
910 file=sys.stderr)
911 sys.exit(1)
912 superproject = git_superproject.Superproject(self.manifest.repodir)
913 try:
914 superproject_shas = superproject.GetAllProjectsSHAs(url=superproject_url)
915 except Exception as e:
916 print('error: Cannot get project SHAs for %s: %s: %s' %
917 (superproject_url, type(e).__name__, str(e)),
918 file=sys.stderr)
919 sys.exit(1)
920 projects_missing_shas = []
921 for project in all_projects:
922 path = project.relpath
923 if not path:
924 continue
925 sha = superproject_shas.get(path)
926 if sha:
927 project.SetRevisionId(sha)
928 else:
929 projects_missing_shas.append(path)
930 if projects_missing_shas:
931 print('error: please file a bug using %s to report missing shas for: %s' %
932 (BUG_REPORT_URL, projects_missing_shas), file=sys.stderr)
933 sys.exit(1)
934
Mike Frysinger5a033082019-09-23 19:21:20 -0400935 err_network_sync = False
936 err_update_projects = False
937 err_checkout = False
938
Dave Borowitz67700e92012-10-23 15:00:54 -0700939 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700940 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700941 to_fetch = []
942 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700943 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700944 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900945 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700946 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700947
Mike Frysinger5a033082019-09-23 19:21:20 -0400948 fetched = self._Fetch(to_fetch, opt, err_event)
949
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500950 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700951 if opt.network_only:
952 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400953 if err_event.isSet():
954 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
955 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700956 return
957
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800958 # Iteratively fetch missing and/or nested unregistered submodules
959 previously_missing_set = set()
960 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100961 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800962 all_projects = self.GetProjects(args,
963 missing_ok=True,
964 submodules_ok=opt.fetch_submodules)
965 missing = []
966 for project in all_projects:
967 if project.gitdir not in fetched:
968 missing.append(project)
969 if not missing:
970 break
971 # Stop us from non-stopped fetching actually-missing repos: If set of
972 # missing repos has not been changed from last fetch, we break.
973 missing_set = set(p.name for p in missing)
974 if previously_missing_set == missing_set:
975 break
976 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400977 fetched.update(self._Fetch(missing, opt, err_event))
978
979 # If we saw an error, exit with code 1 so that other scripts can check.
980 if err_event.isSet():
981 err_network_sync = True
982 if opt.fail_fast:
983 print('\nerror: Exited sync due to fetch errors.\n'
984 'Local checkouts *not* updated. Resolve network issues & '
985 'retry.\n'
986 '`repo sync -l` will update some local checkouts.',
987 file=sys.stderr)
988 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800989
Julien Campergue335f5ef2013-10-16 11:02:35 +0200990 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700991 # bail out now, we have no working tree
992 return
993
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500994 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400995 err_event.set()
996 err_update_projects = True
997 if opt.fail_fast:
998 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
999 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001000
Mike Frysinger5a033082019-09-23 19:21:20 -04001001 err_results = []
1002 self._Checkout(all_projects, opt, err_event, err_results)
1003 if err_event.isSet():
1004 err_checkout = True
1005 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001006
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001007 # If there's a notice that's supposed to print at the end of the sync, print
1008 # it now...
1009 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001010 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001011
Mike Frysinger5a033082019-09-23 19:21:20 -04001012 # If we saw an error, exit with code 1 so that other scripts can check.
1013 if err_event.isSet():
1014 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1015 if err_network_sync:
1016 print('error: Downloading network changes failed.', file=sys.stderr)
1017 if err_update_projects:
1018 print('error: Updating local project lists failed.', file=sys.stderr)
1019 if err_checkout:
1020 print('error: Checking out local projects failed.', file=sys.stderr)
1021 if err_results:
1022 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1023 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1024 file=sys.stderr)
1025 sys.exit(1)
1026
Mike Frysingere19d9e12020-02-12 11:23:32 -05001027 if not opt.quiet:
1028 print('repo sync has finished successfully.')
1029
David Pursehouse819827a2020-02-12 15:20:19 +09001030
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001031def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001032 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001033 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001034 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001035 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001036 if project.Exists:
1037 project.PostRepoUpgrade()
1038
David Pursehouse819827a2020-02-12 15:20:19 +09001039
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001040def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001041 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001042 print('info: A new version of repo is available', file=sys.stderr)
1043 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001044 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001045 syncbuf = SyncBuffer(rp.config)
1046 rp.Sync_LocalHalf(syncbuf)
1047 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001048 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001049 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001050 raise RepoChangedException(['--repo-upgraded'])
1051 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001052 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001053 else:
1054 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001055 print('repo version %s is current' % rp.work_git.describe(HEAD),
1056 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001057
David Pursehouse819827a2020-02-12 15:20:19 +09001058
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001059def _VerifyTag(project):
1060 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1061 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001062 print('warning: GnuPG was not available during last "repo init"\n'
1063 'warning: Cannot automatically authenticate repo."""',
1064 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065 return True
1066
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001068 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001069 except GitError:
1070 cur = None
1071
1072 if not cur \
1073 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001074 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075 if rev.startswith(R_HEADS):
1076 rev = rev[len(R_HEADS):]
1077
Sarah Owenscecd1d82012-11-01 22:59:27 -07001078 print(file=sys.stderr)
1079 print("warning: project '%s' branch '%s' is not signed"
1080 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001081 return False
1082
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001083 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001084 env['GIT_DIR'] = project.gitdir
1085 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001086
1087 cmd = [GIT, 'tag', '-v', cur]
1088 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001089 stdout=subprocess.PIPE,
1090 stderr=subprocess.PIPE,
1091 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001092 out = proc.stdout.read()
1093 proc.stdout.close()
1094
1095 err = proc.stderr.read()
1096 proc.stderr.close()
1097
1098 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001099 print(file=sys.stderr)
1100 print(out, file=sys.stderr)
1101 print(err, file=sys.stderr)
1102 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001103 return False
1104 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001105
David Rileye0684ad2017-04-05 00:02:59 -07001106
Dave Borowitz67700e92012-10-23 15:00:54 -07001107class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001108 _ALPHA = 0.5
1109
Dave Borowitz67700e92012-10-23 15:00:54 -07001110 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001111 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001112 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001113 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001114
1115 def Get(self, project):
1116 self._Load()
1117 return self._times.get(project.name, _ONE_DAY_S)
1118
1119 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001120 self._Load()
1121 name = project.name
1122 old = self._times.get(name, t)
1123 self._seen.add(name)
1124 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001125 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001126
1127 def _Load(self):
1128 if self._times is None:
1129 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001130 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001131 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001132 except (IOError, ValueError):
1133 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001134 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001135 except OSError:
1136 pass
1137 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001138
1139 def Save(self):
1140 if self._times is None:
1141 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001142
1143 to_delete = []
1144 for name in self._times:
1145 if name not in self._seen:
1146 to_delete.append(name)
1147 for name in to_delete:
1148 del self._times[name]
1149
Dave Borowitz67700e92012-10-23 15:00:54 -07001150 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001151 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001152 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001153 except (IOError, TypeError):
1154 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001155 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001156 except OSError:
1157 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001158
1159# This is a replacement for xmlrpc.client.Transport using urllib2
1160# and supporting persistent-http[s]. It cannot change hosts from
1161# request to request like the normal transport, the real url
1162# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001163
1164
Dan Willemsen0745bb22015-08-17 13:41:45 -07001165class PersistentTransport(xmlrpc.client.Transport):
1166 def __init__(self, orig_host):
1167 self.orig_host = orig_host
1168
1169 def request(self, host, handler, request_body, verbose=False):
1170 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1171 # Python doesn't understand cookies with the #HttpOnly_ prefix
1172 # Since we're only using them for HTTP, copy the file temporarily,
1173 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001174 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001175 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001176 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001177 try:
1178 with open(cookiefile) as f:
1179 for line in f:
1180 if line.startswith("#HttpOnly_"):
1181 line = line[len("#HttpOnly_"):]
1182 tmpcookiefile.write(line)
1183 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001184
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001185 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001186 try:
1187 cookiejar.load()
1188 except cookielib.LoadError:
1189 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001190 finally:
1191 tmpcookiefile.close()
1192 else:
1193 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001194
1195 proxyhandler = urllib.request.ProxyHandler
1196 if proxy:
1197 proxyhandler = urllib.request.ProxyHandler({
1198 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001199 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001200
1201 opener = urllib.request.build_opener(
1202 urllib.request.HTTPCookieProcessor(cookiejar),
1203 proxyhandler)
1204
1205 url = urllib.parse.urljoin(self.orig_host, handler)
1206 parse_results = urllib.parse.urlparse(url)
1207
1208 scheme = parse_results.scheme
1209 if scheme == 'persistent-http':
1210 scheme = 'http'
1211 if scheme == 'persistent-https':
1212 # If we're proxying through persistent-https, use http. The
1213 # proxy itself will do the https.
1214 if proxy:
1215 scheme = 'http'
1216 else:
1217 scheme = 'https'
1218
1219 # Parse out any authentication information using the base class
1220 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1221
1222 url = urllib.parse.urlunparse((
1223 scheme,
1224 host,
1225 parse_results.path,
1226 parse_results.params,
1227 parse_results.query,
1228 parse_results.fragment))
1229
1230 request = urllib.request.Request(url, request_body)
1231 if extra_headers is not None:
1232 for (name, header) in extra_headers:
1233 request.add_header(name, header)
1234 request.add_header('Content-Type', 'text/xml')
1235 try:
1236 response = opener.open(request)
1237 except urllib.error.HTTPError as e:
1238 if e.code == 501:
1239 # We may have been redirected through a login process
1240 # but our POST turned into a GET. Retry.
1241 response = opener.open(request)
1242 else:
1243 raise
1244
1245 p, u = xmlrpc.client.getparser()
1246 while 1:
1247 data = response.read(1024)
1248 if not data:
1249 break
1250 p.feed(data)
1251 p.close()
1252 return u.close()
1253
1254 def close(self):
1255 pass