blob: 5655a1e627d12b3395d7b932f807c258a2576cd8 [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
Mike Frysingerae6cb082019-08-27 01:10:59 -0400741 def ValidateOptions(self, opt, args):
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400742 if opt.force_broken:
743 print('warning: -f/--force-broken is now the default behavior, and the '
744 'options are deprecated', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700745 if opt.network_only and opt.detach_head:
Mike Frysingerae6cb082019-08-27 01:10:59 -0400746 self.OptionParser.error('cannot combine -n and -d')
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700747 if opt.network_only and opt.local_only:
Mike Frysingerae6cb082019-08-27 01:10:59 -0400748 self.OptionParser.error('cannot combine -n and -l')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500749 if opt.manifest_name and opt.smart_sync:
Mike Frysingerae6cb082019-08-27 01:10:59 -0400750 self.OptionParser.error('cannot combine -m and -s')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500751 if opt.manifest_name and opt.smart_tag:
Mike Frysingerae6cb082019-08-27 01:10:59 -0400752 self.OptionParser.error('cannot combine -m and -t')
David Pursehousecf76b1b2012-09-14 10:31:42 +0900753 if opt.manifest_server_username or opt.manifest_server_password:
754 if not (opt.smart_sync or opt.smart_tag):
Mike Frysingerae6cb082019-08-27 01:10:59 -0400755 self.OptionParser.error('-u and -p may only be combined with -s or -t')
David Pursehousecf76b1b2012-09-14 10:31:42 +0900756 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Mike Frysingerae6cb082019-08-27 01:10:59 -0400757 self.OptionParser.error('both -u and -p must be given')
758
759 def Execute(self, opt, args):
760 if opt.jobs:
761 self.jobs = opt.jobs
762 if self.jobs > 1:
763 soft_limit, _ = _rlimit_nofile()
764 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500765
766 if opt.manifest_name:
767 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700768
Chirayu Desaia892b102013-06-11 14:18:46 +0530769 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900770 smart_sync_manifest_name = "smart_sync_override.xml"
771 smart_sync_manifest_path = os.path.join(
772 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530773
Victor Boivie08c880d2011-04-19 10:32:52 +0200774 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700775 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900776 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700777 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700778 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900779
780 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900781 if not opt.quiet:
782 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900783
David Pursehouse86d973d2012-08-24 10:21:02 +0900784 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900785 username = None
786 password = None
787 if opt.manifest_server_username and opt.manifest_server_password:
788 username = opt.manifest_server_username
789 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900790 else:
791 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900792 info = netrc.netrc()
793 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900794 # .netrc file does not exist or could not be opened
795 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900796 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900797 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530798 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900799 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900800 auth = info.authenticators(parse_result.hostname)
801 if auth:
802 username, _account, password = auth
803 else:
804 print('No credentials found for %s in .netrc'
805 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700806 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700807 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900808
809 if (username and password):
810 manifest_server = manifest_server.replace('://', '://%s:%s@' %
811 (username, password),
812 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900813
Dan Willemsen0745bb22015-08-17 13:41:45 -0700814 transport = PersistentTransport(manifest_server)
815 if manifest_server.startswith('persistent-'):
816 manifest_server = manifest_server[len('persistent-'):]
817
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700818 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700819 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200820 if opt.smart_sync:
821 p = self.manifest.manifestProject
822 b = p.GetBranch(p.CurrentBranch)
823 branch = b.merge
824 if branch.startswith(R_HEADS):
825 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700826
Victor Boivie08c880d2011-04-19 10:32:52 +0200827 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700828 if 'SYNC_TARGET' in env:
829 target = env['SYNC_TARGET']
830 [success, manifest_str] = server.GetApprovedManifest(branch, target)
831 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200832 target = '%s-%s' % (env['TARGET_PRODUCT'],
833 env['TARGET_BUILD_VARIANT'])
834 [success, manifest_str] = server.GetApprovedManifest(branch, target)
835 else:
836 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700837 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200838 assert(opt.smart_tag)
839 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700840
841 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900842 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700843 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900844 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700845 try:
846 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700847 finally:
848 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900849 except IOError as e:
850 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900851 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700852 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700853 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100854 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700855 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900856 print('error: manifest server RPC call failed: %s' %
857 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700858 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530859 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700860 print('error: cannot connect to manifest server %s:\n%s'
861 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900862 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530863 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700864 print('error: cannot connect to manifest server %s:\n%d %s'
865 % (self.manifest.manifest_server, e.errcode, e.errmsg),
866 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700867 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900868 else: # Not smart sync or smart tag mode
869 if os.path.isfile(smart_sync_manifest_path):
870 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800871 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900872 except OSError as e:
873 print('error: failed to remove existing smart sync override manifest: %s' %
874 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700875
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700876 rp = self.manifest.repoProject
877 rp.PreSync()
878
879 mp = self.manifest.manifestProject
880 mp.PreSync()
881
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800882 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700883 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800884
Nico Sallembien9bb18162009-12-07 15:38:01 -0800885 if not opt.local_only:
David Rileye0684ad2017-04-05 00:02:59 -0700886 start = time.time()
887 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
888 current_branch_only=opt.current_branch_only,
889 no_tags=opt.no_tags,
890 optimized_fetch=opt.optimized_fetch,
Xin Li745be2e2019-06-03 11:24:30 -0700891 submodules=self.manifest.HasSubmodules,
892 clone_filter=self.manifest.CloneFilter)
David Rileye0684ad2017-04-05 00:02:59 -0700893 finish = time.time()
894 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
895 start, finish, success)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800896
897 if mp.HasChanges:
898 syncbuf = SyncBuffer(mp.config)
David Rileye0684ad2017-04-05 00:02:59 -0700899 start = time.time()
Martin Kellye4e94d22017-03-21 16:05:12 -0700900 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
David Rileye0684ad2017-04-05 00:02:59 -0700901 clean = syncbuf.Finish()
902 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
903 start, time.time(), clean)
904 if not clean:
Nico Sallembien9bb18162009-12-07 15:38:01 -0800905 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100906 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700907 if opt.jobs is None:
908 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700909
Simran Basib9a1b732015-08-20 12:19:28 -0700910 if self.gitc_manifest:
911 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700912 missing_ok=True)
913 gitc_projects = []
914 opened_projects = []
915 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700916 if project.relpath in self.gitc_manifest.paths and \
917 self.gitc_manifest.paths[project.relpath].old_revision:
918 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700919 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700920 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700921
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700922 if not args:
923 gitc_projects = None
924
925 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700926 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700927 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
928 if manifest_name:
929 manifest.Override(manifest_name)
930 else:
931 manifest.Override(self.manifest.manifestFile)
932 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
933 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700934 gitc_projects)
935 print('GITC client successfully synced.')
936
937 # The opened projects need to be synced as normal, therefore we
938 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700939 # TODO: make this more reliable -- if there's a project name/path overlap,
940 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900941 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
942 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700943 if not args:
944 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800945 all_projects = self.GetProjects(args,
946 missing_ok=True,
947 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700948
Dave Borowitz67700e92012-10-23 15:00:54 -0700949 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700950 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700951 to_fetch = []
952 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700953 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700954 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900955 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700956 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700957
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800958 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700959 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700960 if opt.network_only:
961 # bail out now; the rest touches the working tree
962 return
963
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800964 # Iteratively fetch missing and/or nested unregistered submodules
965 previously_missing_set = set()
966 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100967 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800968 all_projects = self.GetProjects(args,
969 missing_ok=True,
970 submodules_ok=opt.fetch_submodules)
971 missing = []
972 for project in all_projects:
973 if project.gitdir not in fetched:
974 missing.append(project)
975 if not missing:
976 break
977 # Stop us from non-stopped fetching actually-missing repos: If set of
978 # missing repos has not been changed from last fetch, we break.
979 missing_set = set(p.name for p in missing)
980 if previously_missing_set == missing_set:
981 break
982 previously_missing_set = missing_set
983 fetched.update(self._Fetch(missing, opt))
984
Julien Campergue335f5ef2013-10-16 11:02:35 +0200985 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700986 # bail out now, we have no working tree
987 return
988
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500989 if self.UpdateProjectList(opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700990 sys.exit(1)
991
Xin Li745be2e2019-06-03 11:24:30 -0700992 self._Checkout(all_projects, opt)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700993
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700994 # If there's a notice that's supposed to print at the end of the sync, print
995 # it now...
996 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700997 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700998
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700999def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001000 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001001 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001002 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001003 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001004 if project.Exists:
1005 project.PostRepoUpgrade()
1006
1007def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
1008 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001009 print('info: A new version of repo is available', file=sys.stderr)
1010 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001011 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001012 syncbuf = SyncBuffer(rp.config)
1013 rp.Sync_LocalHalf(syncbuf)
1014 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001015 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001016 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001017 raise RepoChangedException(['--repo-upgraded'])
1018 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001019 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001020 else:
1021 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001022 print('repo version %s is current' % rp.work_git.describe(HEAD),
1023 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001024
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001025def _VerifyTag(project):
1026 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1027 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001028 print('warning: GnuPG was not available during last "repo init"\n'
1029 'warning: Cannot automatically authenticate repo."""',
1030 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001031 return True
1032
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001034 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035 except GitError:
1036 cur = None
1037
1038 if not cur \
1039 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001040 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001041 if rev.startswith(R_HEADS):
1042 rev = rev[len(R_HEADS):]
1043
Sarah Owenscecd1d82012-11-01 22:59:27 -07001044 print(file=sys.stderr)
1045 print("warning: project '%s' branch '%s' is not signed"
1046 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001047 return False
1048
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001049 env = os.environ.copy()
1050 env['GIT_DIR'] = project.gitdir.encode()
1051 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001052
1053 cmd = [GIT, 'tag', '-v', cur]
1054 proc = subprocess.Popen(cmd,
1055 stdout = subprocess.PIPE,
1056 stderr = subprocess.PIPE,
1057 env = env)
1058 out = proc.stdout.read()
1059 proc.stdout.close()
1060
1061 err = proc.stderr.read()
1062 proc.stderr.close()
1063
1064 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001065 print(file=sys.stderr)
1066 print(out, file=sys.stderr)
1067 print(err, file=sys.stderr)
1068 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001069 return False
1070 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001071
David Rileye0684ad2017-04-05 00:02:59 -07001072
Dave Borowitz67700e92012-10-23 15:00:54 -07001073class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001074 _ALPHA = 0.5
1075
Dave Borowitz67700e92012-10-23 15:00:54 -07001076 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001077 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001078 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001079 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001080
1081 def Get(self, project):
1082 self._Load()
1083 return self._times.get(project.name, _ONE_DAY_S)
1084
1085 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001086 self._Load()
1087 name = project.name
1088 old = self._times.get(name, t)
1089 self._seen.add(name)
1090 a = self._ALPHA
1091 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001092
1093 def _Load(self):
1094 if self._times is None:
1095 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001096 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -07001097 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001098 self._times = json.load(f)
1099 finally:
1100 f.close()
1101 except (IOError, ValueError):
1102 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001103 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001104 except OSError:
1105 pass
1106 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001107
1108 def Save(self):
1109 if self._times is None:
1110 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001111
1112 to_delete = []
1113 for name in self._times:
1114 if name not in self._seen:
1115 to_delete.append(name)
1116 for name in to_delete:
1117 del self._times[name]
1118
Dave Borowitz67700e92012-10-23 15:00:54 -07001119 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001120 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -07001121 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001122 json.dump(self._times, f, indent=2)
1123 finally:
1124 f.close()
1125 except (IOError, TypeError):
1126 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001127 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001128 except OSError:
1129 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001130
1131# This is a replacement for xmlrpc.client.Transport using urllib2
1132# and supporting persistent-http[s]. It cannot change hosts from
1133# request to request like the normal transport, the real url
1134# is passed during initialization.
1135class PersistentTransport(xmlrpc.client.Transport):
1136 def __init__(self, orig_host):
1137 self.orig_host = orig_host
1138
1139 def request(self, host, handler, request_body, verbose=False):
1140 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1141 # Python doesn't understand cookies with the #HttpOnly_ prefix
1142 # Since we're only using them for HTTP, copy the file temporarily,
1143 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001144 if cookiefile:
1145 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001146 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001147 try:
1148 with open(cookiefile) as f:
1149 for line in f:
1150 if line.startswith("#HttpOnly_"):
1151 line = line[len("#HttpOnly_"):]
1152 tmpcookiefile.write(line)
1153 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001154
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001155 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001156 try:
1157 cookiejar.load()
1158 except cookielib.LoadError:
1159 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001160 finally:
1161 tmpcookiefile.close()
1162 else:
1163 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001164
1165 proxyhandler = urllib.request.ProxyHandler
1166 if proxy:
1167 proxyhandler = urllib.request.ProxyHandler({
1168 "http": proxy,
1169 "https": proxy })
1170
1171 opener = urllib.request.build_opener(
1172 urllib.request.HTTPCookieProcessor(cookiejar),
1173 proxyhandler)
1174
1175 url = urllib.parse.urljoin(self.orig_host, handler)
1176 parse_results = urllib.parse.urlparse(url)
1177
1178 scheme = parse_results.scheme
1179 if scheme == 'persistent-http':
1180 scheme = 'http'
1181 if scheme == 'persistent-https':
1182 # If we're proxying through persistent-https, use http. The
1183 # proxy itself will do the https.
1184 if proxy:
1185 scheme = 'http'
1186 else:
1187 scheme = 'https'
1188
1189 # Parse out any authentication information using the base class
1190 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1191
1192 url = urllib.parse.urlunparse((
1193 scheme,
1194 host,
1195 parse_results.path,
1196 parse_results.params,
1197 parse_results.query,
1198 parse_results.fragment))
1199
1200 request = urllib.request.Request(url, request_body)
1201 if extra_headers is not None:
1202 for (name, header) in extra_headers:
1203 request.add_header(name, header)
1204 request.add_header('Content-Type', 'text/xml')
1205 try:
1206 response = opener.open(request)
1207 except urllib.error.HTTPError as e:
1208 if e.code == 501:
1209 # We may have been redirected through a login process
1210 # but our POST turned into a GET. Retry.
1211 response = opener.open(request)
1212 else:
1213 raise
1214
1215 p, u = xmlrpc.client.getparser()
1216 while 1:
1217 data = response.read(1024)
1218 if not data:
1219 break
1220 p.feed(data)
1221 p.close()
1222 return u.close()
1223
1224 def close(self):
1225 pass
1226