blob: ae29f085ef6408b2fad43cb2cbf816254ef1f82f [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Mike Frysinger979d5bd2020-02-09 02:28:34 -050018
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
David Pursehouse86d973d2012-08-24 10:21:02 +090020import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070021from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import os
23import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import subprocess
26import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070027import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070028import time
David Pursehouse59bbb582013-05-17 10:49:33 +090029
30from pyversion import is_python3
31if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070032 import http.cookiejar as cookielib
33 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053034 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070035 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090036 import xmlrpc.client
37else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070038 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053039 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070040 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053041 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090042 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053043 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070044 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053045 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070046 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053047 xmlrpc = imp.new_module('xmlrpc')
48 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070049
Roy Lee18afd7f2010-05-09 04:32:08 +080050try:
51 import threading as _threading
52except ImportError:
53 import dummy_threading as _threading
54
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070055try:
56 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090057
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070058 def _rlimit_nofile():
59 return resource.getrlimit(resource.RLIMIT_NOFILE)
60except ImportError:
61 def _rlimit_nofile():
62 return (256, 256)
63
Dave Borowitz18857212012-10-23 17:02:59 -070064try:
65 import multiprocessing
66except ImportError:
67 multiprocessing = None
68
David Rileye0684ad2017-04-05 00:02:59 -070069import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070070from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090071from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090072from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070073import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070074from project import Project
75from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080076from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000077from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070078import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070079from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070080from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080081from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070082from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070083
Dave Borowitz67700e92012-10-23 15:00:54 -070084_ONE_DAY_S = 24 * 60 * 60
85
David Pursehouse819827a2020-02-12 15:20:19 +090086
Doug Andersonfc06ced2011-03-16 15:49:18 -070087class _FetchError(Exception):
88 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
89 pass
90
David Pursehouse819827a2020-02-12 15:20:19 +090091
Xin Li745be2e2019-06-03 11:24:30 -070092class _CheckoutError(Exception):
93 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
94
David Pursehouse819827a2020-02-12 15:20:19 +090095
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080096class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080097 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070098 common = True
99 helpSummary = "Update working tree to the latest revision"
100 helpUsage = """
101%prog [<project>...]
102"""
103 helpDescription = """
104The '%prog' command synchronizes local project directories
105with the remote repositories specified in the manifest. If a local
106project does not yet exist, it will clone a new local directory from
107the remote repository and set up tracking branches as specified in
108the manifest. If the local project already exists, '%prog'
109will update the remote branches and rebase any new local changes
110on top of the new remote changes.
111
112'%prog' will synchronize all projects listed at the command
113line. Projects can be specified either by name, or by a relative
114or absolute path to the project's local directory. If no projects
115are specified, '%prog' will synchronize all projects listed in
116the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700117
118The -d/--detach option can be used to switch specified projects
119back to the manifest revision. This option is especially helpful
120if the project is currently on a topic branch, but the manifest
121revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700122
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700123The -s/--smart-sync option can be used to sync to a known good
124build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200125manifest. The -t/--smart-tag option is similar and allows you to
126specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700127
David Pursehousecf76b1b2012-09-14 10:31:42 +0900128The -u/--manifest-server-username and -p/--manifest-server-password
129options can be used to specify a username and password to authenticate
130with the manifest server when using the -s or -t option.
131
132If -u and -p are not specified when using the -s or -t option, '%prog'
133will attempt to read authentication credentials for the manifest server
134from the user's .netrc file.
135
136'%prog' will not use authentication credentials from -u/-p or .netrc
137if the manifest server specified in the manifest file already includes
138credentials.
139
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400140By default, all projects will be synced. The --fail-fast option can be used
141to halt syncing as soon as possible when the the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500142
Kevin Degiabaa7f32014-11-12 11:27:45 -0700143The --force-sync option can be used to overwrite existing git
144directories if they have previously been linked to a different
145object direcotry. WARNING: This may cause data to be lost since
146refs may be removed when overwriting.
147
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500148The --force-remove-dirty option can be used to remove previously used
149projects with uncommitted changes. WARNING: This may cause data to be
150lost since uncommitted changes may be removed with projects that no longer
151exist in the manifest.
152
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700153The --no-clone-bundle option disables any attempt to use
154$URL/clone.bundle to bootstrap a new Git repository from a
155resumeable bundle file on a content delivery network. This
156may be necessary if there are problems with the local Python
157HTTP client or proxy configuration, but the Git binary works.
158
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800159The --fetch-submodules option enables fetching Git submodules
160of a project from server.
161
David Pursehousef2fad612015-01-29 14:36:28 +0900162The -c/--current-branch option can be used to only fetch objects that
163are on the branch specified by a project's revision.
164
David Pursehouseb1553542014-09-04 21:28:09 +0900165The --optimized-fetch option can be used to only fetch projects that
166are fixed to a sha1 revision if the sha1 revision does not already
167exist locally.
168
David Pursehouse74cfd272015-10-14 10:50:15 +0900169The --prune option can be used to remove any refs that no longer
170exist on the remote.
171
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400172# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700173
174If at least one project remote URL uses an SSH connection (ssh://,
175git+ssh://, or user@host:path syntax) repo will automatically
176enable the SSH ControlMaster option when connecting to that host.
177This feature permits other projects in the same '%prog' session to
178reuse the same SSH tunnel, saving connection setup overheads.
179
180To disable this behavior on UNIX platforms, set the GIT_SSH
181environment variable to 'ssh'. For example:
182
183 export GIT_SSH=ssh
184 %prog
185
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400186# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700187
188This feature is automatically disabled on Windows, due to the lack
189of UNIX domain socket support.
190
191This feature is not compatible with url.insteadof rewrites in the
192user's ~/.gitconfig. '%prog' is currently not able to perform the
193rewrite early enough to establish the ControlMaster tunnel.
194
195If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
196later is required to fix a server side protocol bug.
197
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700198"""
199
Nico Sallembien6623b212010-05-11 12:57:01 -0700200 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000201 try:
202 self.jobs = self.manifest.default.sync_j
203 except ManifestParseError:
204 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700205
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500206 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200207 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400208 help='obsolete option (to be deleted in the future)')
209 p.add_option('--fail-fast',
210 dest='fail_fast', action='store_true',
211 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700212 p.add_option('--force-sync',
213 dest='force_sync', action='store_true',
214 help="overwrite an existing git directory if it needs to "
215 "point to a different object directory. WARNING: this "
216 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500217 p.add_option('--force-remove-dirty',
218 dest='force_remove_dirty', action='store_true',
219 help="force remove projects with uncommitted modifications if "
220 "projects no longer exist in the manifest. "
221 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900222 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700223 dest='local_only', action='store_true',
224 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900225 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100226 dest='mp_update', action='store_false', default='true',
227 help='use the existing manifest checkout as-is. '
228 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900229 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700230 dest='network_only', action='store_true',
231 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900232 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700233 dest='detach_head', action='store_true',
234 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900235 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700236 dest='current_branch_only', action='store_true',
237 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500238 p.add_option('-v', '--verbose',
239 dest='output_mode', action='store_true',
240 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900241 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500242 dest='output_mode', action='store_false',
243 help='only show errors')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900244 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800245 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700246 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500247 p.add_option('-m', '--manifest-name',
248 dest='manifest_name',
249 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700250 p.add_option('--no-clone-bundle',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500251 dest='clone_bundle', default=True, action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700252 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800253 p.add_option('-u', '--manifest-server-username', action='store',
254 dest='manifest_server_username',
255 help='username to authenticate with the manifest server')
256 p.add_option('-p', '--manifest-server-password', action='store',
257 dest='manifest_server_password',
258 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800259 p.add_option('--fetch-submodules',
260 dest='fetch_submodules', action='store_true',
261 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700262 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500263 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700264 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900265 p.add_option('--optimized-fetch',
266 dest='optimized_fetch', action='store_true',
267 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900268 p.add_option('--prune', dest='prune', action='store_true',
269 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700270 if show_smart:
271 p.add_option('-s', '--smart-sync',
272 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900273 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200274 p.add_option('-t', '--smart-tag',
275 dest='smart_tag', action='store',
276 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700277
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700278 g = p.add_option_group('repo Version options')
279 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500280 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700281 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700282 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800283 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700284 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700285
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500286 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700287 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800288
David James8d201162013-10-11 17:03:19 -0700289 Delegates most of the work to _FetchHelper.
290
291 Args:
292 opt: Program options returned from optparse. See _Options().
293 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500294 sem: We'll release() this semaphore when we exit so that another thread
295 can be started up.
David James89ece422014-01-09 18:51:58 -0800296 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700297 _FetchHelper docstring for details.
298 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500299 try:
300 for project in projects:
301 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400302 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500303 break
304 finally:
305 sem.release()
David James8d201162013-10-11 17:03:19 -0700306
Xin Li745be2e2019-06-03 11:24:30 -0700307 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
308 clone_filter):
David James8d201162013-10-11 17:03:19 -0700309 """Fetch git objects for a single project.
310
David Pursehousec1b86a22012-11-14 11:36:51 +0900311 Args:
312 opt: Program options returned from optparse. See _Options().
313 project: Project object for the project to fetch.
314 lock: Lock for accessing objects that are shared amongst multiple
315 _FetchHelper() threads.
316 fetched: set object that we will add project.gitdir to when we're done
317 (with our lock held).
318 pm: Instance of a Project object. We will call pm.update() (with our
319 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900320 err_event: We'll set this event in the case of an error (after printing
321 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700322 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700323
324 Returns:
325 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900326 """
327 # We'll set to true once we've locked the lock.
328 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700329
David Pursehousec1b86a22012-11-14 11:36:51 +0900330 # Encapsulate everything in a try/except/finally so that:
331 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900332 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700333 start = time.time()
334 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900335 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700336 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900337 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900338 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500339 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900340 current_branch_only=opt.current_branch_only,
341 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500342 clone_bundle=opt.clone_bundle,
343 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900344 optimized_fetch=opt.optimized_fetch,
345 prune=opt.prune,
346 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900347 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700348
David Pursehousec1b86a22012-11-14 11:36:51 +0900349 # Lock around all the rest of the code, since printing, updating a set
350 # and Progress.update() are not thread safe.
351 lock.acquire()
352 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700353
David Pursehousec1b86a22012-11-14 11:36:51 +0900354 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800355 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700356 print('error: Cannot fetch %s from %s'
357 % (project.name, project.remote.url),
358 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400359 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900360 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700361
David Pursehousec1b86a22012-11-14 11:36:51 +0900362 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400363 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900364 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800365 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400366 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900367 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900368 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900369 err_event.set()
370 raise
371 finally:
372 if did_lock:
373 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700374 finish = time.time()
375 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
376 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800377
David James8d201162013-10-11 17:03:19 -0700378 return success
379
Mike Frysinger5a033082019-09-23 19:21:20 -0400380 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700381 fetched = set()
David James89ece422014-01-09 18:51:58 -0800382 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200383 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200384 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800385
David James89ece422014-01-09 18:51:58 -0800386 objdir_project_map = dict()
387 for project in projects:
388 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700389
David James89ece422014-01-09 18:51:58 -0800390 threads = set()
391 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800392 for project_list in objdir_project_map.values():
393 # Check for any errors before running any more tasks.
394 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400395 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800396 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700397
David James89ece422014-01-09 18:51:58 -0800398 sem.acquire()
399 kwargs = dict(opt=opt,
400 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500401 sem=sem,
David James89ece422014-01-09 18:51:58 -0800402 lock=lock,
403 fetched=fetched,
404 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700405 err_event=err_event,
406 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800407 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900408 t = _threading.Thread(target=self._FetchProjectList,
409 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200410 # Ensure that Ctrl-C will not freeze the repo process.
411 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800412 threads.add(t)
413 t.start()
David James89ece422014-01-09 18:51:58 -0800414 else:
415 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800416
David James89ece422014-01-09 18:51:58 -0800417 for t in threads:
418 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800419
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700420 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700421 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700422
Julien Campergue335f5ef2013-10-16 11:02:35 +0200423 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400424 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200425
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700426 return fetched
427
Xin Li745be2e2019-06-03 11:24:30 -0700428 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
429 """Main function of the fetch threads.
430
431 Delegates most of the work to _CheckoutOne.
432
433 Args:
434 opt: Program options returned from optparse. See _Options().
435 projects: Projects to fetch.
436 sem: We'll release() this semaphore when we exit so that another thread
437 can be started up.
438 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
439 _CheckoutOne docstring for details.
440 """
441 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400442 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700443 finally:
444 sem.release()
445
Vadim Bendeburydff91942019-11-06 11:05:00 -0800446 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700447 """Checkout work tree for one project
448
449 Args:
450 opt: Program options returned from optparse. See _Options().
451 project: Project object for the project to checkout.
452 lock: Lock for accessing objects that are shared amongst multiple
453 _CheckoutWorker() threads.
454 pm: Instance of a Project object. We will call pm.update() (with our
455 lock held).
456 err_event: We'll set this event in the case of an error (after printing
457 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800458 err_results: A list of strings, paths to git repos where checkout
459 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700460
461 Returns:
462 Whether the fetch was successful.
463 """
464 # We'll set to true once we've locked the lock.
465 did_lock = False
466
Xin Li745be2e2019-06-03 11:24:30 -0700467 # Encapsulate everything in a try/except/finally so that:
468 # - We always set err_event in the case of an exception.
469 # - We always make sure we unlock the lock if we locked it.
470 start = time.time()
471 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
472 detach_head=opt.detach_head)
473 success = False
474 try:
475 try:
476 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700477
478 # Lock around all the rest of the code, since printing, updating a set
479 # and Progress.update() are not thread safe.
480 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400481 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700482 did_lock = True
483
484 if not success:
485 err_event.set()
486 print('error: Cannot checkout %s' % (project.name),
487 file=sys.stderr)
488 raise _CheckoutError()
489
Mike Frysinger3538dd22019-08-26 15:32:06 -0400490 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700491 except _CheckoutError:
492 pass
493 except Exception as e:
494 print('error: Cannot checkout %s: %s: %s' %
495 (project.name, type(e).__name__, str(e)),
496 file=sys.stderr)
497 err_event.set()
498 raise
499 finally:
500 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800501 if not success:
502 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700503 lock.release()
504 finish = time.time()
505 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
506 start, finish, success)
507
508 return success
509
Mike Frysinger5a033082019-09-23 19:21:20 -0400510 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700511 """Checkout projects listed in all_projects
512
513 Args:
514 all_projects: List of all projects that should be checked out.
515 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400516 err_event: We'll set this event in the case of an error (after printing
517 out info about the error).
518 err_results: A list of strings, paths to git repos where checkout
519 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700520 """
521
522 # Perform checkouts in multiple threads when we are using partial clone.
523 # Without partial clone, all needed git objects are already downloaded,
524 # in this situation it's better to use only one process because the checkout
525 # would be mostly disk I/O; with partial clone, the objects are only
526 # downloaded when demanded (at checkout time), which is similar to the
527 # Sync_NetworkHalf case and parallelism would be helpful.
528 if self.manifest.CloneFilter:
529 syncjobs = self.jobs
530 else:
531 syncjobs = 1
532
533 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400534 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700535
536 threads = set()
537 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700538
539 for project in all_projects:
540 # Check for any errors before running any more tasks.
541 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400542 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700543 break
544
545 sem.acquire()
546 if project.worktree:
547 kwargs = dict(opt=opt,
548 sem=sem,
549 project=project,
550 lock=lock,
551 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800552 err_event=err_event,
553 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700554 if syncjobs > 1:
555 t = _threading.Thread(target=self._CheckoutWorker,
556 kwargs=kwargs)
557 # Ensure that Ctrl-C will not freeze the repo process.
558 t.daemon = True
559 threads.add(t)
560 t.start()
561 else:
562 self._CheckoutWorker(**kwargs)
563
564 for t in threads:
565 t.join()
566
567 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700568
Mike Frysinger5a033082019-09-23 19:21:20 -0400569 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700570 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700571 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500572 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500573 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900574 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500575 print('%s: Shared project %s found, disabling pruning.' %
576 (project.relpath, project.name))
577 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500578 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500579 else:
580 # This isn't perfect, but it's the best we can do with old git.
581 print('%s: WARNING: shared projects are unreliable when using old '
582 'versions of git; please upgrade to git-2.7.0+.'
583 % (project.relpath,),
584 file=sys.stderr)
585 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700586 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700587
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500588 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700589 cpu_count = multiprocessing.cpu_count()
590 else:
591 cpu_count = 1
592 jobs = min(self.jobs, cpu_count)
593
594 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700595 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700596 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700597 return
598
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400599 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700600
601 threads = set()
602 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700603
David James8d201162013-10-11 17:03:19 -0700604 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700605 try:
606 try:
David James8d201162013-10-11 17:03:19 -0700607 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700608 except GitError:
609 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900610 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700611 err_event.set()
612 raise
613 finally:
614 sem.release()
615
Gabe Black2ff30292014-10-09 17:54:35 -0700616 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400617 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700618 break
619 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700620 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700621 t.daemon = True
622 threads.add(t)
623 t.start()
624
625 for t in threads:
626 t.join()
627
Tim Kilbourn07669002013-03-08 15:02:49 -0800628 def _ReloadManifest(self, manifest_name=None):
629 if manifest_name:
630 # Override calls _Unload already
631 self.manifest.Override(manifest_name)
632 else:
633 self.manifest._Unload()
634
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500635 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700636 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700637 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700638 if project.relpath:
639 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700640 file_name = 'project.list'
641 file_path = os.path.join(self.manifest.repodir, file_name)
642 old_project_paths = []
643
644 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500645 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700646 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800647 # In reversed order, so subfolders are deleted before parent folder.
648 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700649 if not path:
650 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700651 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900652 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700653 gitdir = os.path.join(self.manifest.topdir, path, '.git')
654 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900655 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900656 manifest=self.manifest,
657 name=path,
658 remote=RemoteSpec('origin'),
659 gitdir=gitdir,
660 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500661 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900662 worktree=os.path.join(self.manifest.topdir, path),
663 relpath=path,
664 revisionExpr='HEAD',
665 revisionId=None,
666 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500667 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900668 quiet=opt.quiet,
669 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400670 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700671
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700672 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500673 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700674 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700675 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700676 return 0
677
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400678 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
679 if not self.manifest.manifest_server:
680 print('error: cannot smart sync: no manifest server defined in '
681 'manifest', file=sys.stderr)
682 sys.exit(1)
683
684 manifest_server = self.manifest.manifest_server
685 if not opt.quiet:
686 print('Using manifest server %s' % manifest_server)
687
David Pursehouseeeff3532020-02-12 11:24:10 +0900688 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400689 username = None
690 password = None
691 if opt.manifest_server_username and opt.manifest_server_password:
692 username = opt.manifest_server_username
693 password = opt.manifest_server_password
694 else:
695 try:
696 info = netrc.netrc()
697 except IOError:
698 # .netrc file does not exist or could not be opened
699 pass
700 else:
701 try:
702 parse_result = urllib.parse.urlparse(manifest_server)
703 if parse_result.hostname:
704 auth = info.authenticators(parse_result.hostname)
705 if auth:
706 username, _account, password = auth
707 else:
708 print('No credentials found for %s in .netrc'
709 % parse_result.hostname, file=sys.stderr)
710 except netrc.NetrcParseError as e:
711 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
712
713 if (username and password):
714 manifest_server = manifest_server.replace('://', '://%s:%s@' %
715 (username, password),
716 1)
717
718 transport = PersistentTransport(manifest_server)
719 if manifest_server.startswith('persistent-'):
720 manifest_server = manifest_server[len('persistent-'):]
721
722 try:
723 server = xmlrpc.client.Server(manifest_server, transport=transport)
724 if opt.smart_sync:
725 p = self.manifest.manifestProject
726 b = p.GetBranch(p.CurrentBranch)
727 branch = b.merge
728 if branch.startswith(R_HEADS):
729 branch = branch[len(R_HEADS):]
730
Mike Frysinger56ce3462019-12-04 19:30:48 -0500731 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500732 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400733 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500734 elif ('TARGET_PRODUCT' in os.environ and
735 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500736 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
737 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400738 [success, manifest_str] = server.GetApprovedManifest(branch, target)
739 else:
740 [success, manifest_str] = server.GetApprovedManifest(branch)
741 else:
742 assert(opt.smart_tag)
743 [success, manifest_str] = server.GetManifest(opt.smart_tag)
744
745 if success:
746 manifest_name = os.path.basename(smart_sync_manifest_path)
747 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500748 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400749 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400750 except IOError as e:
751 print('error: cannot write manifest to %s:\n%s'
752 % (smart_sync_manifest_path, e),
753 file=sys.stderr)
754 sys.exit(1)
755 self._ReloadManifest(manifest_name)
756 else:
757 print('error: manifest server RPC call failed: %s' %
758 manifest_str, file=sys.stderr)
759 sys.exit(1)
760 except (socket.error, IOError, xmlrpc.client.Fault) as e:
761 print('error: cannot connect to manifest server %s:\n%s'
762 % (self.manifest.manifest_server, e), file=sys.stderr)
763 sys.exit(1)
764 except xmlrpc.client.ProtocolError as e:
765 print('error: cannot connect to manifest server %s:\n%d %s'
766 % (self.manifest.manifest_server, e.errcode, e.errmsg),
767 file=sys.stderr)
768 sys.exit(1)
769
770 return manifest_name
771
Mike Frysingerfb527e32019-08-27 02:34:32 -0400772 def _UpdateManifestProject(self, opt, mp, manifest_name):
773 """Fetch & update the local manifest project."""
774 if not opt.local_only:
775 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500776 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400777 current_branch_only=opt.current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500778 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400779 optimized_fetch=opt.optimized_fetch,
780 submodules=self.manifest.HasSubmodules,
781 clone_filter=self.manifest.CloneFilter)
782 finish = time.time()
783 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
784 start, finish, success)
785
786 if mp.HasChanges:
787 syncbuf = SyncBuffer(mp.config)
788 start = time.time()
789 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
790 clean = syncbuf.Finish()
791 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
792 start, time.time(), clean)
793 if not clean:
794 sys.exit(1)
795 self._ReloadManifest(opt.manifest_name)
796 if opt.jobs is None:
797 self.jobs = self.manifest.default.sync_j
798
Mike Frysingerae6cb082019-08-27 01:10:59 -0400799 def ValidateOptions(self, opt, args):
800 if opt.force_broken:
801 print('warning: -f/--force-broken is now the default behavior, and the '
802 'options are deprecated', file=sys.stderr)
803 if opt.network_only and opt.detach_head:
804 self.OptionParser.error('cannot combine -n and -d')
805 if opt.network_only and opt.local_only:
806 self.OptionParser.error('cannot combine -n and -l')
807 if opt.manifest_name and opt.smart_sync:
808 self.OptionParser.error('cannot combine -m and -s')
809 if opt.manifest_name and opt.smart_tag:
810 self.OptionParser.error('cannot combine -m and -t')
811 if opt.manifest_server_username or opt.manifest_server_password:
812 if not (opt.smart_sync or opt.smart_tag):
813 self.OptionParser.error('-u and -p may only be combined with -s or -t')
814 if None in [opt.manifest_server_username, opt.manifest_server_password]:
815 self.OptionParser.error('both -u and -p must be given')
816
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700817 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800818 if opt.jobs:
819 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700820 if self.jobs > 1:
821 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400822 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700823
Mike Frysinger521d01b2020-02-17 01:51:49 -0500824 opt.quiet = opt.output_mode is False
825 opt.verbose = opt.output_mode is True
826
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500827 if opt.manifest_name:
828 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700829
Chirayu Desaia892b102013-06-11 14:18:46 +0530830 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900831 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900832 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530833
Victor Boivie08c880d2011-04-19 10:32:52 +0200834 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400835 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
836 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900837 if os.path.isfile(smart_sync_manifest_path):
838 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800839 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900840 except OSError as e:
841 print('error: failed to remove existing smart sync override manifest: %s' %
842 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700843
Mike Frysinger5a033082019-09-23 19:21:20 -0400844 err_event = _threading.Event()
845
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700846 rp = self.manifest.repoProject
847 rp.PreSync()
848
849 mp = self.manifest.manifestProject
850 mp.PreSync()
851
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800852 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700853 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800854
Fredrik de Grootcc960972019-11-22 09:04:31 +0100855 if not opt.mp_update:
856 print('Skipping update of local manifest project.')
857 else:
858 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700859
Simran Basib9a1b732015-08-20 12:19:28 -0700860 if self.gitc_manifest:
861 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700862 missing_ok=True)
863 gitc_projects = []
864 opened_projects = []
865 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700866 if project.relpath in self.gitc_manifest.paths and \
867 self.gitc_manifest.paths[project.relpath].old_revision:
868 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700869 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700870 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700871
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700872 if not args:
873 gitc_projects = None
874
875 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700876 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700877 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
878 if manifest_name:
879 manifest.Override(manifest_name)
880 else:
881 manifest.Override(self.manifest.manifestFile)
882 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
883 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700884 gitc_projects)
885 print('GITC client successfully synced.')
886
887 # The opened projects need to be synced as normal, therefore we
888 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700889 # TODO: make this more reliable -- if there's a project name/path overlap,
890 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900891 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
892 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700893 if not args:
894 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800895 all_projects = self.GetProjects(args,
896 missing_ok=True,
897 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700898
Mike Frysinger5a033082019-09-23 19:21:20 -0400899 err_network_sync = False
900 err_update_projects = False
901 err_checkout = False
902
Dave Borowitz67700e92012-10-23 15:00:54 -0700903 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700904 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700905 to_fetch = []
906 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700907 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700908 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900909 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700910 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700911
Mike Frysinger5a033082019-09-23 19:21:20 -0400912 fetched = self._Fetch(to_fetch, opt, err_event)
913
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500914 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700915 if opt.network_only:
916 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400917 if err_event.isSet():
918 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
919 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700920 return
921
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800922 # Iteratively fetch missing and/or nested unregistered submodules
923 previously_missing_set = set()
924 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100925 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800926 all_projects = self.GetProjects(args,
927 missing_ok=True,
928 submodules_ok=opt.fetch_submodules)
929 missing = []
930 for project in all_projects:
931 if project.gitdir not in fetched:
932 missing.append(project)
933 if not missing:
934 break
935 # Stop us from non-stopped fetching actually-missing repos: If set of
936 # missing repos has not been changed from last fetch, we break.
937 missing_set = set(p.name for p in missing)
938 if previously_missing_set == missing_set:
939 break
940 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400941 fetched.update(self._Fetch(missing, opt, err_event))
942
943 # If we saw an error, exit with code 1 so that other scripts can check.
944 if err_event.isSet():
945 err_network_sync = True
946 if opt.fail_fast:
947 print('\nerror: Exited sync due to fetch errors.\n'
948 'Local checkouts *not* updated. Resolve network issues & '
949 'retry.\n'
950 '`repo sync -l` will update some local checkouts.',
951 file=sys.stderr)
952 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800953
Julien Campergue335f5ef2013-10-16 11:02:35 +0200954 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700955 # bail out now, we have no working tree
956 return
957
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500958 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400959 err_event.set()
960 err_update_projects = True
961 if opt.fail_fast:
962 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
963 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700964
Mike Frysinger5a033082019-09-23 19:21:20 -0400965 err_results = []
966 self._Checkout(all_projects, opt, err_event, err_results)
967 if err_event.isSet():
968 err_checkout = True
969 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700970
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700971 # If there's a notice that's supposed to print at the end of the sync, print
972 # it now...
973 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700974 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700975
Mike Frysinger5a033082019-09-23 19:21:20 -0400976 # If we saw an error, exit with code 1 so that other scripts can check.
977 if err_event.isSet():
978 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
979 if err_network_sync:
980 print('error: Downloading network changes failed.', file=sys.stderr)
981 if err_update_projects:
982 print('error: Updating local project lists failed.', file=sys.stderr)
983 if err_checkout:
984 print('error: Checking out local projects failed.', file=sys.stderr)
985 if err_results:
986 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
987 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
988 file=sys.stderr)
989 sys.exit(1)
990
Mike Frysingere19d9e12020-02-12 11:23:32 -0500991 if not opt.quiet:
992 print('repo sync has finished successfully.')
993
David Pursehouse819827a2020-02-12 15:20:19 +0900994
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700995def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800996 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700997 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700998 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800999 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001000 if project.Exists:
1001 project.PostRepoUpgrade()
1002
David Pursehouse819827a2020-02-12 15:20:19 +09001003
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001004def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001005 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001006 print('info: A new version of repo is available', file=sys.stderr)
1007 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001008 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001009 syncbuf = SyncBuffer(rp.config)
1010 rp.Sync_LocalHalf(syncbuf)
1011 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001012 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001013 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001014 raise RepoChangedException(['--repo-upgraded'])
1015 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001016 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001017 else:
1018 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001019 print('repo version %s is current' % rp.work_git.describe(HEAD),
1020 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001021
David Pursehouse819827a2020-02-12 15:20:19 +09001022
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001023def _VerifyTag(project):
1024 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1025 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001026 print('warning: GnuPG was not available during last "repo init"\n'
1027 'warning: Cannot automatically authenticate repo."""',
1028 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001029 return True
1030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001031 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001032 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033 except GitError:
1034 cur = None
1035
1036 if not cur \
1037 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001038 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001039 if rev.startswith(R_HEADS):
1040 rev = rev[len(R_HEADS):]
1041
Sarah Owenscecd1d82012-11-01 22:59:27 -07001042 print(file=sys.stderr)
1043 print("warning: project '%s' branch '%s' is not signed"
1044 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001045 return False
1046
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001047 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001048 env['GIT_DIR'] = project.gitdir
1049 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001050
1051 cmd = [GIT, 'tag', '-v', cur]
1052 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001053 stdout=subprocess.PIPE,
1054 stderr=subprocess.PIPE,
1055 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001056 out = proc.stdout.read()
1057 proc.stdout.close()
1058
1059 err = proc.stderr.read()
1060 proc.stderr.close()
1061
1062 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001063 print(file=sys.stderr)
1064 print(out, file=sys.stderr)
1065 print(err, file=sys.stderr)
1066 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067 return False
1068 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001069
David Rileye0684ad2017-04-05 00:02:59 -07001070
Dave Borowitz67700e92012-10-23 15:00:54 -07001071class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001072 _ALPHA = 0.5
1073
Dave Borowitz67700e92012-10-23 15:00:54 -07001074 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001075 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001076 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001077 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001078
1079 def Get(self, project):
1080 self._Load()
1081 return self._times.get(project.name, _ONE_DAY_S)
1082
1083 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001084 self._Load()
1085 name = project.name
1086 old = self._times.get(name, t)
1087 self._seen.add(name)
1088 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001089 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001090
1091 def _Load(self):
1092 if self._times is None:
1093 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001094 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001095 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001096 except (IOError, ValueError):
1097 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001098 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001099 except OSError:
1100 pass
1101 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001102
1103 def Save(self):
1104 if self._times is None:
1105 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001106
1107 to_delete = []
1108 for name in self._times:
1109 if name not in self._seen:
1110 to_delete.append(name)
1111 for name in to_delete:
1112 del self._times[name]
1113
Dave Borowitz67700e92012-10-23 15:00:54 -07001114 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001115 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001116 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001117 except (IOError, TypeError):
1118 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001119 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001120 except OSError:
1121 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001122
1123# This is a replacement for xmlrpc.client.Transport using urllib2
1124# and supporting persistent-http[s]. It cannot change hosts from
1125# request to request like the normal transport, the real url
1126# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001127
1128
Dan Willemsen0745bb22015-08-17 13:41:45 -07001129class PersistentTransport(xmlrpc.client.Transport):
1130 def __init__(self, orig_host):
1131 self.orig_host = orig_host
1132
1133 def request(self, host, handler, request_body, verbose=False):
1134 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1135 # Python doesn't understand cookies with the #HttpOnly_ prefix
1136 # Since we're only using them for HTTP, copy the file temporarily,
1137 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001138 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001139 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001140 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001141 try:
1142 with open(cookiefile) as f:
1143 for line in f:
1144 if line.startswith("#HttpOnly_"):
1145 line = line[len("#HttpOnly_"):]
1146 tmpcookiefile.write(line)
1147 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001148
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001149 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001150 try:
1151 cookiejar.load()
1152 except cookielib.LoadError:
1153 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001154 finally:
1155 tmpcookiefile.close()
1156 else:
1157 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001158
1159 proxyhandler = urllib.request.ProxyHandler
1160 if proxy:
1161 proxyhandler = urllib.request.ProxyHandler({
1162 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001163 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001164
1165 opener = urllib.request.build_opener(
1166 urllib.request.HTTPCookieProcessor(cookiejar),
1167 proxyhandler)
1168
1169 url = urllib.parse.urljoin(self.orig_host, handler)
1170 parse_results = urllib.parse.urlparse(url)
1171
1172 scheme = parse_results.scheme
1173 if scheme == 'persistent-http':
1174 scheme = 'http'
1175 if scheme == 'persistent-https':
1176 # If we're proxying through persistent-https, use http. The
1177 # proxy itself will do the https.
1178 if proxy:
1179 scheme = 'http'
1180 else:
1181 scheme = 'https'
1182
1183 # Parse out any authentication information using the base class
1184 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1185
1186 url = urllib.parse.urlunparse((
1187 scheme,
1188 host,
1189 parse_results.path,
1190 parse_results.params,
1191 parse_results.query,
1192 parse_results.fragment))
1193
1194 request = urllib.request.Request(url, request_body)
1195 if extra_headers is not None:
1196 for (name, header) in extra_headers:
1197 request.add_header(name, header)
1198 request.add_header('Content-Type', 'text/xml')
1199 try:
1200 response = opener.open(request)
1201 except urllib.error.HTTPError as e:
1202 if e.code == 501:
1203 # We may have been redirected through a login process
1204 # but our POST turned into a GET. Retry.
1205 response = opener.open(request)
1206 else:
1207 raise
1208
1209 p, u = xmlrpc.client.getparser()
1210 while 1:
1211 data = response.read(1024)
1212 if not data:
1213 break
1214 p.feed(data)
1215 p.close()
1216 return u.close()
1217
1218 def close(self):
1219 pass