blob: dadf2e4547d21387308d957d2d380b60789e393e [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 [...]
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()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500848 cb = rp.CurrentBranch
849 if cb:
850 base = rp.GetBranch(cb).merge
851 if not base or not base.startswith('refs/heads/'):
852 print('warning: repo is not tracking a remote branch, so it will not '
853 'receive updates; run `repo init --repo-branch=stable` to fix.',
854 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700855
856 mp = self.manifest.manifestProject
857 mp.PreSync()
858
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800859 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700860 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800861
Fredrik de Grootcc960972019-11-22 09:04:31 +0100862 if not opt.mp_update:
863 print('Skipping update of local manifest project.')
864 else:
865 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700866
Simran Basib9a1b732015-08-20 12:19:28 -0700867 if self.gitc_manifest:
868 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700869 missing_ok=True)
870 gitc_projects = []
871 opened_projects = []
872 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700873 if project.relpath in self.gitc_manifest.paths and \
874 self.gitc_manifest.paths[project.relpath].old_revision:
875 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700876 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700877 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700878
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700879 if not args:
880 gitc_projects = None
881
882 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700883 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700884 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
885 if manifest_name:
886 manifest.Override(manifest_name)
887 else:
888 manifest.Override(self.manifest.manifestFile)
889 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
890 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700891 gitc_projects)
892 print('GITC client successfully synced.')
893
894 # The opened projects need to be synced as normal, therefore we
895 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700896 # TODO: make this more reliable -- if there's a project name/path overlap,
897 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900898 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
899 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700900 if not args:
901 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800902 all_projects = self.GetProjects(args,
903 missing_ok=True,
904 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700905
Mike Frysinger5a033082019-09-23 19:21:20 -0400906 err_network_sync = False
907 err_update_projects = False
908 err_checkout = False
909
Dave Borowitz67700e92012-10-23 15:00:54 -0700910 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700911 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700912 to_fetch = []
913 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700914 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700915 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900916 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700917 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700918
Mike Frysinger5a033082019-09-23 19:21:20 -0400919 fetched = self._Fetch(to_fetch, opt, err_event)
920
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500921 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700922 if opt.network_only:
923 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400924 if err_event.isSet():
925 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
926 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700927 return
928
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800929 # Iteratively fetch missing and/or nested unregistered submodules
930 previously_missing_set = set()
931 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100932 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800933 all_projects = self.GetProjects(args,
934 missing_ok=True,
935 submodules_ok=opt.fetch_submodules)
936 missing = []
937 for project in all_projects:
938 if project.gitdir not in fetched:
939 missing.append(project)
940 if not missing:
941 break
942 # Stop us from non-stopped fetching actually-missing repos: If set of
943 # missing repos has not been changed from last fetch, we break.
944 missing_set = set(p.name for p in missing)
945 if previously_missing_set == missing_set:
946 break
947 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400948 fetched.update(self._Fetch(missing, opt, err_event))
949
950 # If we saw an error, exit with code 1 so that other scripts can check.
951 if err_event.isSet():
952 err_network_sync = True
953 if opt.fail_fast:
954 print('\nerror: Exited sync due to fetch errors.\n'
955 'Local checkouts *not* updated. Resolve network issues & '
956 'retry.\n'
957 '`repo sync -l` will update some local checkouts.',
958 file=sys.stderr)
959 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800960
Julien Campergue335f5ef2013-10-16 11:02:35 +0200961 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700962 # bail out now, we have no working tree
963 return
964
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500965 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400966 err_event.set()
967 err_update_projects = True
968 if opt.fail_fast:
969 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
970 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700971
Mike Frysinger5a033082019-09-23 19:21:20 -0400972 err_results = []
973 self._Checkout(all_projects, opt, err_event, err_results)
974 if err_event.isSet():
975 err_checkout = True
976 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700977
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700978 # If there's a notice that's supposed to print at the end of the sync, print
979 # it now...
980 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700981 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700982
Mike Frysinger5a033082019-09-23 19:21:20 -0400983 # If we saw an error, exit with code 1 so that other scripts can check.
984 if err_event.isSet():
985 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
986 if err_network_sync:
987 print('error: Downloading network changes failed.', file=sys.stderr)
988 if err_update_projects:
989 print('error: Updating local project lists failed.', file=sys.stderr)
990 if err_checkout:
991 print('error: Checking out local projects failed.', file=sys.stderr)
992 if err_results:
993 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
994 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
995 file=sys.stderr)
996 sys.exit(1)
997
Mike Frysingere19d9e12020-02-12 11:23:32 -0500998 if not opt.quiet:
999 print('repo sync has finished successfully.')
1000
David Pursehouse819827a2020-02-12 15:20:19 +09001001
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001002def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001003 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001004 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001005 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001006 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001007 if project.Exists:
1008 project.PostRepoUpgrade()
1009
David Pursehouse819827a2020-02-12 15:20:19 +09001010
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001011def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001012 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001013 print('info: A new version of repo is available', file=sys.stderr)
1014 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001015 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001016 syncbuf = SyncBuffer(rp.config)
1017 rp.Sync_LocalHalf(syncbuf)
1018 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001019 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001020 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001021 raise RepoChangedException(['--repo-upgraded'])
1022 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001023 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001024 else:
1025 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001026 print('repo version %s is current' % rp.work_git.describe(HEAD),
1027 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001028
David Pursehouse819827a2020-02-12 15:20:19 +09001029
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001030def _VerifyTag(project):
1031 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1032 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001033 print('warning: GnuPG was not available during last "repo init"\n'
1034 'warning: Cannot automatically authenticate repo."""',
1035 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 return True
1037
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001038 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001039 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001040 except GitError:
1041 cur = None
1042
1043 if not cur \
1044 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001045 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001046 if rev.startswith(R_HEADS):
1047 rev = rev[len(R_HEADS):]
1048
Sarah Owenscecd1d82012-11-01 22:59:27 -07001049 print(file=sys.stderr)
1050 print("warning: project '%s' branch '%s' is not signed"
1051 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001052 return False
1053
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001054 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001055 env['GIT_DIR'] = project.gitdir
1056 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001057
1058 cmd = [GIT, 'tag', '-v', cur]
1059 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001060 stdout=subprocess.PIPE,
1061 stderr=subprocess.PIPE,
1062 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063 out = proc.stdout.read()
1064 proc.stdout.close()
1065
1066 err = proc.stderr.read()
1067 proc.stderr.close()
1068
1069 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001070 print(file=sys.stderr)
1071 print(out, file=sys.stderr)
1072 print(err, file=sys.stderr)
1073 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001074 return False
1075 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001076
David Rileye0684ad2017-04-05 00:02:59 -07001077
Dave Borowitz67700e92012-10-23 15:00:54 -07001078class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001079 _ALPHA = 0.5
1080
Dave Borowitz67700e92012-10-23 15:00:54 -07001081 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001082 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001083 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001084 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001085
1086 def Get(self, project):
1087 self._Load()
1088 return self._times.get(project.name, _ONE_DAY_S)
1089
1090 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001091 self._Load()
1092 name = project.name
1093 old = self._times.get(name, t)
1094 self._seen.add(name)
1095 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001096 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001097
1098 def _Load(self):
1099 if self._times is None:
1100 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001101 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001102 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001103 except (IOError, ValueError):
1104 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001105 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001106 except OSError:
1107 pass
1108 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001109
1110 def Save(self):
1111 if self._times is None:
1112 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001113
1114 to_delete = []
1115 for name in self._times:
1116 if name not in self._seen:
1117 to_delete.append(name)
1118 for name in to_delete:
1119 del self._times[name]
1120
Dave Borowitz67700e92012-10-23 15:00:54 -07001121 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001122 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001123 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001124 except (IOError, TypeError):
1125 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001126 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001127 except OSError:
1128 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001129
1130# This is a replacement for xmlrpc.client.Transport using urllib2
1131# and supporting persistent-http[s]. It cannot change hosts from
1132# request to request like the normal transport, the real url
1133# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001134
1135
Dan Willemsen0745bb22015-08-17 13:41:45 -07001136class PersistentTransport(xmlrpc.client.Transport):
1137 def __init__(self, orig_host):
1138 self.orig_host = orig_host
1139
1140 def request(self, host, handler, request_body, verbose=False):
1141 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1142 # Python doesn't understand cookies with the #HttpOnly_ prefix
1143 # Since we're only using them for HTTP, copy the file temporarily,
1144 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001145 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001146 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001147 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001148 try:
1149 with open(cookiefile) as f:
1150 for line in f:
1151 if line.startswith("#HttpOnly_"):
1152 line = line[len("#HttpOnly_"):]
1153 tmpcookiefile.write(line)
1154 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001155
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001156 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001157 try:
1158 cookiejar.load()
1159 except cookielib.LoadError:
1160 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001161 finally:
1162 tmpcookiefile.close()
1163 else:
1164 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001165
1166 proxyhandler = urllib.request.ProxyHandler
1167 if proxy:
1168 proxyhandler = urllib.request.ProxyHandler({
1169 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001170 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001171
1172 opener = urllib.request.build_opener(
1173 urllib.request.HTTPCookieProcessor(cookiejar),
1174 proxyhandler)
1175
1176 url = urllib.parse.urljoin(self.orig_host, handler)
1177 parse_results = urllib.parse.urlparse(url)
1178
1179 scheme = parse_results.scheme
1180 if scheme == 'persistent-http':
1181 scheme = 'http'
1182 if scheme == 'persistent-https':
1183 # If we're proxying through persistent-https, use http. The
1184 # proxy itself will do the https.
1185 if proxy:
1186 scheme = 'http'
1187 else:
1188 scheme = 'https'
1189
1190 # Parse out any authentication information using the base class
1191 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1192
1193 url = urllib.parse.urlunparse((
1194 scheme,
1195 host,
1196 parse_results.path,
1197 parse_results.params,
1198 parse_results.query,
1199 parse_results.fragment))
1200
1201 request = urllib.request.Request(url, request_body)
1202 if extra_headers is not None:
1203 for (name, header) in extra_headers:
1204 request.add_header(name, header)
1205 request.add_header('Content-Type', 'text/xml')
1206 try:
1207 response = opener.open(request)
1208 except urllib.error.HTTPError as e:
1209 if e.code == 501:
1210 # We may have been redirected through a login process
1211 # but our POST turned into a GET. Retry.
1212 response = opener.open(request)
1213 else:
1214 raise
1215
1216 p, u = xmlrpc.client.getparser()
1217 while 1:
1218 data = response.read(1024)
1219 if not data:
1220 break
1221 p.feed(data)
1222 p.close()
1223 return u.close()
1224
1225 def close(self):
1226 pass