blob: 3eab2fcfa512380656c451d2c0f54174971fac24 [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
Anthony King85b24ac2014-05-06 15:57:48 +010018import json
David Pursehouse86d973d2012-08-24 10:21:02 +090019import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070020from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import os
22import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
David Pursehouse59bbb582013-05-17 10:49:33 +090028
29from pyversion import is_python3
30if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070031 import http.cookiejar as cookielib
32 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070034 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpc.client
36else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070037 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070039 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090041 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053042 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070043 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053044 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070045 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053046 xmlrpc = imp.new_module('xmlrpc')
47 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070048
Roy Lee18afd7f2010-05-09 04:32:08 +080049try:
50 import threading as _threading
51except ImportError:
52 import dummy_threading as _threading
53
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070054try:
55 import resource
56 def _rlimit_nofile():
57 return resource.getrlimit(resource.RLIMIT_NOFILE)
58except ImportError:
59 def _rlimit_nofile():
60 return (256, 256)
61
Dave Borowitz18857212012-10-23 17:02:59 -070062try:
63 import multiprocessing
64except ImportError:
65 multiprocessing = None
66
David Rileye0684ad2017-04-05 00:02:59 -070067import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070068from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090069from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090070from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070071import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070072from project import Project
73from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080074from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000075from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070076import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070077from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070078from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080079from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070080from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081
Dave Borowitz67700e92012-10-23 15:00:54 -070082_ONE_DAY_S = 24 * 60 * 60
83
Doug Andersonfc06ced2011-03-16 15:49:18 -070084class _FetchError(Exception):
85 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
86 pass
87
Xin Li745be2e2019-06-03 11:24:30 -070088class _CheckoutError(Exception):
89 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
90
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080091class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080092 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070093 common = True
94 helpSummary = "Update working tree to the latest revision"
95 helpUsage = """
96%prog [<project>...]
97"""
98 helpDescription = """
99The '%prog' command synchronizes local project directories
100with the remote repositories specified in the manifest. If a local
101project does not yet exist, it will clone a new local directory from
102the remote repository and set up tracking branches as specified in
103the manifest. If the local project already exists, '%prog'
104will update the remote branches and rebase any new local changes
105on top of the new remote changes.
106
107'%prog' will synchronize all projects listed at the command
108line. Projects can be specified either by name, or by a relative
109or absolute path to the project's local directory. If no projects
110are specified, '%prog' will synchronize all projects listed in
111the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700112
113The -d/--detach option can be used to switch specified projects
114back to the manifest revision. This option is especially helpful
115if the project is currently on a topic branch, but the manifest
116revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700117
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700118The -s/--smart-sync option can be used to sync to a known good
119build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200120manifest. The -t/--smart-tag option is similar and allows you to
121specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700122
David Pursehousecf76b1b2012-09-14 10:31:42 +0900123The -u/--manifest-server-username and -p/--manifest-server-password
124options can be used to specify a username and password to authenticate
125with the manifest server when using the -s or -t option.
126
127If -u and -p are not specified when using the -s or -t option, '%prog'
128will attempt to read authentication credentials for the manifest server
129from the user's .netrc file.
130
131'%prog' will not use authentication credentials from -u/-p or .netrc
132if the manifest server specified in the manifest file already includes
133credentials.
134
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400135By default, all projects will be synced. The --fail-fast option can be used
136to halt syncing as soon as possible when the the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500137
Kevin Degiabaa7f32014-11-12 11:27:45 -0700138The --force-sync option can be used to overwrite existing git
139directories if they have previously been linked to a different
140object direcotry. WARNING: This may cause data to be lost since
141refs may be removed when overwriting.
142
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500143The --force-remove-dirty option can be used to remove previously used
144projects with uncommitted changes. WARNING: This may cause data to be
145lost since uncommitted changes may be removed with projects that no longer
146exist in the manifest.
147
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700148The --no-clone-bundle option disables any attempt to use
149$URL/clone.bundle to bootstrap a new Git repository from a
150resumeable bundle file on a content delivery network. This
151may be necessary if there are problems with the local Python
152HTTP client or proxy configuration, but the Git binary works.
153
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800154The --fetch-submodules option enables fetching Git submodules
155of a project from server.
156
David Pursehousef2fad612015-01-29 14:36:28 +0900157The -c/--current-branch option can be used to only fetch objects that
158are on the branch specified by a project's revision.
159
David Pursehouseb1553542014-09-04 21:28:09 +0900160The --optimized-fetch option can be used to only fetch projects that
161are fixed to a sha1 revision if the sha1 revision does not already
162exist locally.
163
David Pursehouse74cfd272015-10-14 10:50:15 +0900164The --prune option can be used to remove any refs that no longer
165exist on the remote.
166
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400167# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700168
169If at least one project remote URL uses an SSH connection (ssh://,
170git+ssh://, or user@host:path syntax) repo will automatically
171enable the SSH ControlMaster option when connecting to that host.
172This feature permits other projects in the same '%prog' session to
173reuse the same SSH tunnel, saving connection setup overheads.
174
175To disable this behavior on UNIX platforms, set the GIT_SSH
176environment variable to 'ssh'. For example:
177
178 export GIT_SSH=ssh
179 %prog
180
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400181# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700182
183This feature is automatically disabled on Windows, due to the lack
184of UNIX domain socket support.
185
186This feature is not compatible with url.insteadof rewrites in the
187user's ~/.gitconfig. '%prog' is currently not able to perform the
188rewrite early enough to establish the ControlMaster tunnel.
189
190If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
191later is required to fix a server side protocol bug.
192
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700193"""
194
Nico Sallembien6623b212010-05-11 12:57:01 -0700195 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000196 try:
197 self.jobs = self.manifest.default.sync_j
198 except ManifestParseError:
199 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700200
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500201 p.add_option('-f', '--force-broken',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400202 help='obsolete option (to be deleted in the future)')
203 p.add_option('--fail-fast',
204 dest='fail_fast', action='store_true',
205 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700206 p.add_option('--force-sync',
207 dest='force_sync', action='store_true',
208 help="overwrite an existing git directory if it needs to "
209 "point to a different object directory. WARNING: this "
210 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500211 p.add_option('--force-remove-dirty',
212 dest='force_remove_dirty', action='store_true',
213 help="force remove projects with uncommitted modifications if "
214 "projects no longer exist in the manifest. "
215 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900216 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700217 dest='local_only', action='store_true',
218 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900219 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700220 dest='network_only', action='store_true',
221 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900222 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700223 dest='detach_head', action='store_true',
224 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900225 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700226 dest='current_branch_only', action='store_true',
227 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900228 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700229 dest='quiet', action='store_true',
230 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900231 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800232 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700233 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500234 p.add_option('-m', '--manifest-name',
235 dest='manifest_name',
236 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700237 p.add_option('--no-clone-bundle',
238 dest='no_clone_bundle', action='store_true',
239 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800240 p.add_option('-u', '--manifest-server-username', action='store',
241 dest='manifest_server_username',
242 help='username to authenticate with the manifest server')
243 p.add_option('-p', '--manifest-server-password', action='store',
244 dest='manifest_server_password',
245 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800246 p.add_option('--fetch-submodules',
247 dest='fetch_submodules', action='store_true',
248 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700249 p.add_option('--no-tags',
250 dest='no_tags', action='store_true',
251 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900252 p.add_option('--optimized-fetch',
253 dest='optimized_fetch', action='store_true',
254 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900255 p.add_option('--prune', dest='prune', action='store_true',
256 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700257 if show_smart:
258 p.add_option('-s', '--smart-sync',
259 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900260 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200261 p.add_option('-t', '--smart-tag',
262 dest='smart_tag', action='store',
263 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700264
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700265 g = p.add_option_group('repo Version options')
266 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267 dest='no_repo_verify', action='store_true',
268 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700269 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800270 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700271 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700272
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500273 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700274 """Main function of the fetch threads.
Doug Andersonfc06ced2011-03-16 15:49:18 -0700275
David James8d201162013-10-11 17:03:19 -0700276 Delegates most of the work to _FetchHelper.
277
278 Args:
279 opt: Program options returned from optparse. See _Options().
280 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500281 sem: We'll release() this semaphore when we exit so that another thread
282 can be started up.
David James89ece422014-01-09 18:51:58 -0800283 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700284 _FetchHelper docstring for details.
285 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500286 try:
287 for project in projects:
288 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400289 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500290 break
291 finally:
292 sem.release()
David James8d201162013-10-11 17:03:19 -0700293
Xin Li745be2e2019-06-03 11:24:30 -0700294 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
295 clone_filter):
David James8d201162013-10-11 17:03:19 -0700296 """Fetch git objects for a single project.
297
David Pursehousec1b86a22012-11-14 11:36:51 +0900298 Args:
299 opt: Program options returned from optparse. See _Options().
300 project: Project object for the project to fetch.
301 lock: Lock for accessing objects that are shared amongst multiple
302 _FetchHelper() threads.
303 fetched: set object that we will add project.gitdir to when we're done
304 (with our lock held).
305 pm: Instance of a Project object. We will call pm.update() (with our
306 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900307 err_event: We'll set this event in the case of an error (after printing
308 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700309 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700310
311 Returns:
312 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900313 """
314 # We'll set to true once we've locked the lock.
315 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700316
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530317 if not opt.quiet:
318 print('Fetching project %s' % project.name)
319
David Pursehousec1b86a22012-11-14 11:36:51 +0900320 # Encapsulate everything in a try/except/finally so that:
321 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900322 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700323 start = time.time()
324 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900325 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700326 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900327 success = project.Sync_NetworkHalf(
328 quiet=opt.quiet,
329 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700330 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700331 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900332 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
David Pursehouse74cfd272015-10-14 10:50:15 +0900333 optimized_fetch=opt.optimized_fetch,
Xin Li745be2e2019-06-03 11:24:30 -0700334 prune=opt.prune,
335 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900336 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700337
David Pursehousec1b86a22012-11-14 11:36:51 +0900338 # Lock around all the rest of the code, since printing, updating a set
339 # and Progress.update() are not thread safe.
340 lock.acquire()
341 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700342
David Pursehousec1b86a22012-11-14 11:36:51 +0900343 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800344 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700345 print('error: Cannot fetch %s from %s'
346 % (project.name, project.remote.url),
347 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400348 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900349 raise _FetchError()
Roy Lee18afd7f2010-05-09 04:32:08 +0800350
David Pursehousec1b86a22012-11-14 11:36:51 +0900351 fetched.add(project.gitdir)
352 pm.update()
353 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800354 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400355 except Exception as e:
356 print('error: Cannot fetch %s (%s: %s)' \
357 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900358 err_event.set()
359 raise
360 finally:
361 if did_lock:
362 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700363 finish = time.time()
364 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
365 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800366
David James8d201162013-10-11 17:03:19 -0700367 return success
368
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700369 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700370 fetched = set()
David James89ece422014-01-09 18:51:58 -0800371 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200372 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200373 print_newline=not(opt.quiet),
374 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800375
David James89ece422014-01-09 18:51:58 -0800376 objdir_project_map = dict()
377 for project in projects:
378 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700379
David James89ece422014-01-09 18:51:58 -0800380 threads = set()
381 sem = _threading.Semaphore(self.jobs)
382 err_event = _threading.Event()
383 for project_list in objdir_project_map.values():
384 # Check for any errors before running any more tasks.
385 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400386 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800387 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700388
David James89ece422014-01-09 18:51:58 -0800389 sem.acquire()
390 kwargs = dict(opt=opt,
391 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500392 sem=sem,
David James89ece422014-01-09 18:51:58 -0800393 lock=lock,
394 fetched=fetched,
395 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700396 err_event=err_event,
397 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800398 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700399 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800400 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200401 # Ensure that Ctrl-C will not freeze the repo process.
402 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800403 threads.add(t)
404 t.start()
David James89ece422014-01-09 18:51:58 -0800405 else:
406 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800407
David James89ece422014-01-09 18:51:58 -0800408 for t in threads:
409 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800410
David James89ece422014-01-09 18:51:58 -0800411 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400412 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800413 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
414 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700415
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700416 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700417 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700418
Julien Campergue335f5ef2013-10-16 11:02:35 +0200419 if not self.manifest.IsArchive:
420 self._GCProjects(projects)
421
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700422 return fetched
423
Xin Li745be2e2019-06-03 11:24:30 -0700424 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
425 """Main function of the fetch threads.
426
427 Delegates most of the work to _CheckoutOne.
428
429 Args:
430 opt: Program options returned from optparse. See _Options().
431 projects: Projects to fetch.
432 sem: We'll release() this semaphore when we exit so that another thread
433 can be started up.
434 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
435 _CheckoutOne docstring for details.
436 """
437 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400438 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700439 finally:
440 sem.release()
441
442 def _CheckoutOne(self, opt, project, lock, pm, err_event):
443 """Checkout work tree for one project
444
445 Args:
446 opt: Program options returned from optparse. See _Options().
447 project: Project object for the project to checkout.
448 lock: Lock for accessing objects that are shared amongst multiple
449 _CheckoutWorker() threads.
450 pm: Instance of a Project object. We will call pm.update() (with our
451 lock held).
452 err_event: We'll set this event in the case of an error (after printing
453 out info about the error).
454
455 Returns:
456 Whether the fetch was successful.
457 """
458 # We'll set to true once we've locked the lock.
459 did_lock = False
460
461 if not opt.quiet:
462 print('Checking out project %s' % project.name)
463
464 # Encapsulate everything in a try/except/finally so that:
465 # - We always set err_event in the case of an exception.
466 # - We always make sure we unlock the lock if we locked it.
467 start = time.time()
468 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
469 detach_head=opt.detach_head)
470 success = False
471 try:
472 try:
473 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
474 success = syncbuf.Finish()
475
476 # Lock around all the rest of the code, since printing, updating a set
477 # and Progress.update() are not thread safe.
478 lock.acquire()
479 did_lock = True
480
481 if not success:
482 err_event.set()
483 print('error: Cannot checkout %s' % (project.name),
484 file=sys.stderr)
485 raise _CheckoutError()
486
487 pm.update()
488 except _CheckoutError:
489 pass
490 except Exception as e:
491 print('error: Cannot checkout %s: %s: %s' %
492 (project.name, type(e).__name__, str(e)),
493 file=sys.stderr)
494 err_event.set()
495 raise
496 finally:
497 if did_lock:
498 lock.release()
499 finish = time.time()
500 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
501 start, finish, success)
502
503 return success
504
505 def _Checkout(self, all_projects, opt):
506 """Checkout projects listed in all_projects
507
508 Args:
509 all_projects: List of all projects that should be checked out.
510 opt: Program options returned from optparse. See _Options().
511 """
512
513 # Perform checkouts in multiple threads when we are using partial clone.
514 # Without partial clone, all needed git objects are already downloaded,
515 # in this situation it's better to use only one process because the checkout
516 # would be mostly disk I/O; with partial clone, the objects are only
517 # downloaded when demanded (at checkout time), which is similar to the
518 # Sync_NetworkHalf case and parallelism would be helpful.
519 if self.manifest.CloneFilter:
520 syncjobs = self.jobs
521 else:
522 syncjobs = 1
523
524 lock = _threading.Lock()
525 pm = Progress('Syncing work tree', len(all_projects))
526
527 threads = set()
528 sem = _threading.Semaphore(syncjobs)
529 err_event = _threading.Event()
530
531 for project in all_projects:
532 # Check for any errors before running any more tasks.
533 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400534 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700535 break
536
537 sem.acquire()
538 if project.worktree:
539 kwargs = dict(opt=opt,
540 sem=sem,
541 project=project,
542 lock=lock,
543 pm=pm,
544 err_event=err_event)
545 if syncjobs > 1:
546 t = _threading.Thread(target=self._CheckoutWorker,
547 kwargs=kwargs)
548 # Ensure that Ctrl-C will not freeze the repo process.
549 t.daemon = True
550 threads.add(t)
551 t.start()
552 else:
553 self._CheckoutWorker(**kwargs)
554
555 for t in threads:
556 t.join()
557
558 pm.end()
559 # If we saw an error, exit with code 1 so that other scripts can check.
560 if err_event.isSet():
561 print('\nerror: Exited sync due to checkout errors', file=sys.stderr)
562 sys.exit(1)
563
Dave Borowitz18857212012-10-23 17:02:59 -0700564 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700565 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700566 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700567 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
568 print('Shared project %s found, disabling pruning.' % project.name)
569 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
570 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700571
Dave Borowitze2152672012-10-31 12:24:38 -0700572 has_dash_c = git_require((1, 7, 2))
573 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700574 cpu_count = multiprocessing.cpu_count()
575 else:
576 cpu_count = 1
577 jobs = min(self.jobs, cpu_count)
578
579 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700580 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700581 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700582 return
583
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400584 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700585
586 threads = set()
587 sem = _threading.Semaphore(jobs)
588 err_event = _threading.Event()
589
David James8d201162013-10-11 17:03:19 -0700590 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700591 try:
592 try:
David James8d201162013-10-11 17:03:19 -0700593 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700594 except GitError:
595 err_event.set()
596 except:
597 err_event.set()
598 raise
599 finally:
600 sem.release()
601
Gabe Black2ff30292014-10-09 17:54:35 -0700602 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700603 if err_event.isSet():
604 break
605 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700606 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700607 t.daemon = True
608 threads.add(t)
609 t.start()
610
611 for t in threads:
612 t.join()
613
614 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700615 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700616 sys.exit(1)
617
Tim Kilbourn07669002013-03-08 15:02:49 -0800618 def _ReloadManifest(self, manifest_name=None):
619 if manifest_name:
620 # Override calls _Unload already
621 self.manifest.Override(manifest_name)
622 else:
623 self.manifest._Unload()
624
Dan Willemsen43507912016-09-01 16:26:02 -0700625 def _DeleteProject(self, path):
626 print('Deleting obsolete path %s' % path, file=sys.stderr)
627
628 # Delete the .git directory first, so we're less likely to have a partially
629 # working git repository around. There shouldn't be any git projects here,
630 # so rmtree works.
631 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -0700632 platform_utils.rmtree(os.path.join(path, '.git'))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700633 except OSError as e:
634 print('Failed to remove %s (%s)' % (os.path.join(path, '.git'), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700635 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
636 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400637 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700638
639 # Delete everything under the worktree, except for directories that contain
640 # another git project
641 dirs_to_remove = []
642 failed = False
Renaud Paquaybed8b622018-09-27 10:46:58 -0700643 for root, dirs, files in platform_utils.walk(path):
Dan Willemsen43507912016-09-01 16:26:02 -0700644 for f in files:
645 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800646 platform_utils.remove(os.path.join(root, f))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700647 except OSError as e:
648 print('Failed to remove %s (%s)' % (os.path.join(root, f), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700649 failed = True
650 dirs[:] = [d for d in dirs
651 if not os.path.lexists(os.path.join(root, d, '.git'))]
652 dirs_to_remove += [os.path.join(root, d) for d in dirs
653 if os.path.join(root, d) not in dirs_to_remove]
654 for d in reversed(dirs_to_remove):
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700655 if platform_utils.islink(d):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700656 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800657 platform_utils.remove(d)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700658 except OSError as e:
659 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700660 failed = True
Renaud Paquaybed8b622018-09-27 10:46:58 -0700661 elif len(platform_utils.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700662 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700663 platform_utils.rmdir(d)
664 except OSError as e:
665 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700666 failed = True
667 continue
668 if failed:
669 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
670 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400671 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700672
673 # Try deleting parent dirs if they are empty
674 project_dir = path
675 while project_dir != self.manifest.topdir:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700676 if len(platform_utils.listdir(project_dir)) == 0:
677 platform_utils.rmdir(project_dir)
Dan Willemsen43507912016-09-01 16:26:02 -0700678 else:
679 break
680 project_dir = os.path.dirname(project_dir)
681
682 return 0
683
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500684 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700685 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700686 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700687 if project.relpath:
688 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700689 file_name = 'project.list'
690 file_path = os.path.join(self.manifest.repodir, file_name)
691 old_project_paths = []
692
693 if os.path.exists(file_path):
694 fd = open(file_path, 'r')
695 try:
696 old_project_paths = fd.read().split('\n')
697 finally:
698 fd.close()
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800699 # In reversed order, so subfolders are deleted before parent folder.
700 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700701 if not path:
702 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700703 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900704 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700705 gitdir = os.path.join(self.manifest.topdir, path, '.git')
706 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900707 project = Project(
708 manifest = self.manifest,
709 name = path,
710 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700711 gitdir = gitdir,
712 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900713 worktree = os.path.join(self.manifest.topdir, path),
714 relpath = path,
715 revisionExpr = 'HEAD',
716 revisionId = None,
717 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400718
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500719 if project.IsDirty() and opt.force_remove_dirty:
720 print('WARNING: Removing dirty project "%s": uncommitted changes '
721 'erased' % project.relpath, file=sys.stderr)
722 self._DeleteProject(project.worktree)
723 elif project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900724 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900725 'are present' % project.relpath, file=sys.stderr)
726 print(' commit changes, then run sync again',
727 file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400728 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700729 elif self._DeleteProject(project.worktree):
Mike Frysingera850ca22019-08-07 17:19:24 -0400730 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700731
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700732 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700733 fd = open(file_path, 'w')
734 try:
735 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700736 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700737 finally:
738 fd.close()
739 return 0
740
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700741 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800742 if opt.jobs:
743 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700744 if self.jobs > 1:
745 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400746 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700747
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400748 if opt.force_broken:
749 print('warning: -f/--force-broken is now the default behavior, and the '
750 'options are deprecated', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700751 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700752 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700753 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700754 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700755 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700756 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500757 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700758 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500759 sys.exit(1)
760 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700761 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500762 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900763 if opt.manifest_server_username or opt.manifest_server_password:
764 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700765 print('error: -u and -p may only be combined with -s or -t',
766 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900767 sys.exit(1)
768 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700769 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900770 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500771
772 if opt.manifest_name:
773 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700774
Chirayu Desaia892b102013-06-11 14:18:46 +0530775 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900776 smart_sync_manifest_name = "smart_sync_override.xml"
777 smart_sync_manifest_path = os.path.join(
778 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530779
Victor Boivie08c880d2011-04-19 10:32:52 +0200780 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700781 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900782 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700783 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700784 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900785
786 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900787 if not opt.quiet:
788 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900789
David Pursehouse86d973d2012-08-24 10:21:02 +0900790 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900791 username = None
792 password = None
793 if opt.manifest_server_username and opt.manifest_server_password:
794 username = opt.manifest_server_username
795 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900796 else:
797 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900798 info = netrc.netrc()
799 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900800 # .netrc file does not exist or could not be opened
801 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900802 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900803 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530804 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900805 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900806 auth = info.authenticators(parse_result.hostname)
807 if auth:
808 username, _account, password = auth
809 else:
810 print('No credentials found for %s in .netrc'
811 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700812 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700813 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900814
815 if (username and password):
816 manifest_server = manifest_server.replace('://', '://%s:%s@' %
817 (username, password),
818 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900819
Dan Willemsen0745bb22015-08-17 13:41:45 -0700820 transport = PersistentTransport(manifest_server)
821 if manifest_server.startswith('persistent-'):
822 manifest_server = manifest_server[len('persistent-'):]
823
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700824 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700825 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200826 if opt.smart_sync:
827 p = self.manifest.manifestProject
828 b = p.GetBranch(p.CurrentBranch)
829 branch = b.merge
830 if branch.startswith(R_HEADS):
831 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700832
Victor Boivie08c880d2011-04-19 10:32:52 +0200833 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700834 if 'SYNC_TARGET' in env:
835 target = env['SYNC_TARGET']
836 [success, manifest_str] = server.GetApprovedManifest(branch, target)
837 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200838 target = '%s-%s' % (env['TARGET_PRODUCT'],
839 env['TARGET_BUILD_VARIANT'])
840 [success, manifest_str] = server.GetApprovedManifest(branch, target)
841 else:
842 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700843 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200844 assert(opt.smart_tag)
845 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700846
847 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900848 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700849 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900850 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700851 try:
852 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700853 finally:
854 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900855 except IOError as e:
856 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900857 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700858 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700859 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100860 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700861 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900862 print('error: manifest server RPC call failed: %s' %
863 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700864 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530865 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700866 print('error: cannot connect to manifest server %s:\n%s'
867 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900868 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530869 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700870 print('error: cannot connect to manifest server %s:\n%d %s'
871 % (self.manifest.manifest_server, e.errcode, e.errmsg),
872 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700873 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900874 else: # Not smart sync or smart tag mode
875 if os.path.isfile(smart_sync_manifest_path):
876 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800877 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900878 except OSError as e:
879 print('error: failed to remove existing smart sync override manifest: %s' %
880 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700881
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882 rp = self.manifest.repoProject
883 rp.PreSync()
884
885 mp = self.manifest.manifestProject
886 mp.PreSync()
887
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800888 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700889 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800890
Nico Sallembien9bb18162009-12-07 15:38:01 -0800891 if not opt.local_only:
David Rileye0684ad2017-04-05 00:02:59 -0700892 start = time.time()
893 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
894 current_branch_only=opt.current_branch_only,
895 no_tags=opt.no_tags,
896 optimized_fetch=opt.optimized_fetch,
Xin Li745be2e2019-06-03 11:24:30 -0700897 submodules=self.manifest.HasSubmodules,
898 clone_filter=self.manifest.CloneFilter)
David Rileye0684ad2017-04-05 00:02:59 -0700899 finish = time.time()
900 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
901 start, finish, success)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800902
903 if mp.HasChanges:
904 syncbuf = SyncBuffer(mp.config)
David Rileye0684ad2017-04-05 00:02:59 -0700905 start = time.time()
Martin Kellye4e94d22017-03-21 16:05:12 -0700906 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
David Rileye0684ad2017-04-05 00:02:59 -0700907 clean = syncbuf.Finish()
908 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
909 start, time.time(), clean)
910 if not clean:
Nico Sallembien9bb18162009-12-07 15:38:01 -0800911 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100912 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700913 if opt.jobs is None:
914 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700915
Simran Basib9a1b732015-08-20 12:19:28 -0700916 if self.gitc_manifest:
917 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700918 missing_ok=True)
919 gitc_projects = []
920 opened_projects = []
921 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700922 if project.relpath in self.gitc_manifest.paths and \
923 self.gitc_manifest.paths[project.relpath].old_revision:
924 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700925 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700926 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700927
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700928 if not args:
929 gitc_projects = None
930
931 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700932 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700933 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
934 if manifest_name:
935 manifest.Override(manifest_name)
936 else:
937 manifest.Override(self.manifest.manifestFile)
938 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
939 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700940 gitc_projects)
941 print('GITC client successfully synced.')
942
943 # The opened projects need to be synced as normal, therefore we
944 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700945 # TODO: make this more reliable -- if there's a project name/path overlap,
946 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900947 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
948 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700949 if not args:
950 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800951 all_projects = self.GetProjects(args,
952 missing_ok=True,
953 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700954
Dave Borowitz67700e92012-10-23 15:00:54 -0700955 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700956 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700957 to_fetch = []
958 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700959 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700960 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900961 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700962 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700963
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800964 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700965 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700966 if opt.network_only:
967 # bail out now; the rest touches the working tree
968 return
969
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800970 # Iteratively fetch missing and/or nested unregistered submodules
971 previously_missing_set = set()
972 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100973 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800974 all_projects = self.GetProjects(args,
975 missing_ok=True,
976 submodules_ok=opt.fetch_submodules)
977 missing = []
978 for project in all_projects:
979 if project.gitdir not in fetched:
980 missing.append(project)
981 if not missing:
982 break
983 # Stop us from non-stopped fetching actually-missing repos: If set of
984 # missing repos has not been changed from last fetch, we break.
985 missing_set = set(p.name for p in missing)
986 if previously_missing_set == missing_set:
987 break
988 previously_missing_set = missing_set
989 fetched.update(self._Fetch(missing, opt))
990
Julien Campergue335f5ef2013-10-16 11:02:35 +0200991 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700992 # bail out now, we have no working tree
993 return
994
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500995 if self.UpdateProjectList(opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700996 sys.exit(1)
997
Xin Li745be2e2019-06-03 11:24:30 -0700998 self._Checkout(all_projects, opt)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700999
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001000 # If there's a notice that's supposed to print at the end of the sync, print
1001 # it now...
1002 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001003 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001004
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001005def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001006 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001007 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001008 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001009 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001010 if project.Exists:
1011 project.PostRepoUpgrade()
1012
1013def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
1014 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001015 print('info: A new version of repo is available', file=sys.stderr)
1016 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001017 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001018 syncbuf = SyncBuffer(rp.config)
1019 rp.Sync_LocalHalf(syncbuf)
1020 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001021 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001022 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001023 raise RepoChangedException(['--repo-upgraded'])
1024 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001025 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001026 else:
1027 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001028 print('repo version %s is current' % rp.work_git.describe(HEAD),
1029 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001031def _VerifyTag(project):
1032 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1033 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001034 print('warning: GnuPG was not available during last "repo init"\n'
1035 'warning: Cannot automatically authenticate repo."""',
1036 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001037 return True
1038
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001039 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001040 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001041 except GitError:
1042 cur = None
1043
1044 if not cur \
1045 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001046 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001047 if rev.startswith(R_HEADS):
1048 rev = rev[len(R_HEADS):]
1049
Sarah Owenscecd1d82012-11-01 22:59:27 -07001050 print(file=sys.stderr)
1051 print("warning: project '%s' branch '%s' is not signed"
1052 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001053 return False
1054
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001055 env = os.environ.copy()
1056 env['GIT_DIR'] = project.gitdir.encode()
1057 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001058
1059 cmd = [GIT, 'tag', '-v', cur]
1060 proc = subprocess.Popen(cmd,
1061 stdout = subprocess.PIPE,
1062 stderr = subprocess.PIPE,
1063 env = env)
1064 out = proc.stdout.read()
1065 proc.stdout.close()
1066
1067 err = proc.stderr.read()
1068 proc.stderr.close()
1069
1070 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001071 print(file=sys.stderr)
1072 print(out, file=sys.stderr)
1073 print(err, file=sys.stderr)
1074 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075 return False
1076 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001077
David Rileye0684ad2017-04-05 00:02:59 -07001078
Dave Borowitz67700e92012-10-23 15:00:54 -07001079class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001080 _ALPHA = 0.5
1081
Dave Borowitz67700e92012-10-23 15:00:54 -07001082 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001083 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001084 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001085 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001086
1087 def Get(self, project):
1088 self._Load()
1089 return self._times.get(project.name, _ONE_DAY_S)
1090
1091 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001092 self._Load()
1093 name = project.name
1094 old = self._times.get(name, t)
1095 self._seen.add(name)
1096 a = self._ALPHA
1097 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001098
1099 def _Load(self):
1100 if self._times is None:
1101 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001102 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -07001103 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001104 self._times = json.load(f)
1105 finally:
1106 f.close()
1107 except (IOError, ValueError):
1108 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001109 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001110 except OSError:
1111 pass
1112 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001113
1114 def Save(self):
1115 if self._times is None:
1116 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001117
1118 to_delete = []
1119 for name in self._times:
1120 if name not in self._seen:
1121 to_delete.append(name)
1122 for name in to_delete:
1123 del self._times[name]
1124
Dave Borowitz67700e92012-10-23 15:00:54 -07001125 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001126 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -07001127 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001128 json.dump(self._times, f, indent=2)
1129 finally:
1130 f.close()
1131 except (IOError, TypeError):
1132 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001133 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001134 except OSError:
1135 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001136
1137# This is a replacement for xmlrpc.client.Transport using urllib2
1138# and supporting persistent-http[s]. It cannot change hosts from
1139# request to request like the normal transport, the real url
1140# is passed during initialization.
1141class PersistentTransport(xmlrpc.client.Transport):
1142 def __init__(self, orig_host):
1143 self.orig_host = orig_host
1144
1145 def request(self, host, handler, request_body, verbose=False):
1146 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1147 # Python doesn't understand cookies with the #HttpOnly_ prefix
1148 # Since we're only using them for HTTP, copy the file temporarily,
1149 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001150 if cookiefile:
1151 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001152 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001153 try:
1154 with open(cookiefile) as f:
1155 for line in f:
1156 if line.startswith("#HttpOnly_"):
1157 line = line[len("#HttpOnly_"):]
1158 tmpcookiefile.write(line)
1159 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001160
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001161 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001162 try:
1163 cookiejar.load()
1164 except cookielib.LoadError:
1165 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001166 finally:
1167 tmpcookiefile.close()
1168 else:
1169 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001170
1171 proxyhandler = urllib.request.ProxyHandler
1172 if proxy:
1173 proxyhandler = urllib.request.ProxyHandler({
1174 "http": proxy,
1175 "https": proxy })
1176
1177 opener = urllib.request.build_opener(
1178 urllib.request.HTTPCookieProcessor(cookiejar),
1179 proxyhandler)
1180
1181 url = urllib.parse.urljoin(self.orig_host, handler)
1182 parse_results = urllib.parse.urlparse(url)
1183
1184 scheme = parse_results.scheme
1185 if scheme == 'persistent-http':
1186 scheme = 'http'
1187 if scheme == 'persistent-https':
1188 # If we're proxying through persistent-https, use http. The
1189 # proxy itself will do the https.
1190 if proxy:
1191 scheme = 'http'
1192 else:
1193 scheme = 'https'
1194
1195 # Parse out any authentication information using the base class
1196 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1197
1198 url = urllib.parse.urlunparse((
1199 scheme,
1200 host,
1201 parse_results.path,
1202 parse_results.params,
1203 parse_results.query,
1204 parse_results.fragment))
1205
1206 request = urllib.request.Request(url, request_body)
1207 if extra_headers is not None:
1208 for (name, header) in extra_headers:
1209 request.add_header(name, header)
1210 request.add_header('Content-Type', 'text/xml')
1211 try:
1212 response = opener.open(request)
1213 except urllib.error.HTTPError as e:
1214 if e.code == 501:
1215 # We may have been redirected through a login process
1216 # but our POST turned into a GET. Retry.
1217 response = opener.open(request)
1218 else:
1219 raise
1220
1221 p, u = xmlrpc.client.getparser()
1222 while 1:
1223 data = response.read(1024)
1224 if not data:
1225 break
1226 p.feed(data)
1227 p.close()
1228 return u.close()
1229
1230 def close(self):
1231 pass
1232