blob: 3b4c23c5db28171acdce0328404d01055fc7317a [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
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500135The -f/--force-broken option can be used to proceed with syncing
136other projects if a project sync fails.
137
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',
202 dest='force_broken', action='store_true',
203 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700204 p.add_option('--force-sync',
205 dest='force_sync', action='store_true',
206 help="overwrite an existing git directory if it needs to "
207 "point to a different object directory. WARNING: this "
208 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500209 p.add_option('--force-remove-dirty',
210 dest='force_remove_dirty', action='store_true',
211 help="force remove projects with uncommitted modifications if "
212 "projects no longer exist in the manifest. "
213 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700215 dest='local_only', action='store_true',
216 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700218 dest='network_only', action='store_true',
219 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900220 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700221 dest='detach_head', action='store_true',
222 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900223 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700224 dest='current_branch_only', action='store_true',
225 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900226 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700227 dest='quiet', action='store_true',
228 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900229 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800230 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700231 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500232 p.add_option('-m', '--manifest-name',
233 dest='manifest_name',
234 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700235 p.add_option('--no-clone-bundle',
236 dest='no_clone_bundle', action='store_true',
237 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800238 p.add_option('-u', '--manifest-server-username', action='store',
239 dest='manifest_server_username',
240 help='username to authenticate with the manifest server')
241 p.add_option('-p', '--manifest-server-password', action='store',
242 dest='manifest_server_password',
243 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800244 p.add_option('--fetch-submodules',
245 dest='fetch_submodules', action='store_true',
246 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700247 p.add_option('--no-tags',
248 dest='no_tags', action='store_true',
249 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900250 p.add_option('--optimized-fetch',
251 dest='optimized_fetch', action='store_true',
252 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900253 p.add_option('--prune', dest='prune', action='store_true',
254 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700255 if show_smart:
256 p.add_option('-s', '--smart-sync',
257 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900258 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200259 p.add_option('-t', '--smart-tag',
260 dest='smart_tag', action='store',
261 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700262
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700263 g = p.add_option_group('repo Version options')
264 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700265 dest='no_repo_verify', action='store_true',
266 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700267 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800268 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700269 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500271 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700272 """Main function of the fetch threads.
Doug Andersonfc06ced2011-03-16 15:49:18 -0700273
David James8d201162013-10-11 17:03:19 -0700274 Delegates most of the work to _FetchHelper.
275
276 Args:
277 opt: Program options returned from optparse. See _Options().
278 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500279 sem: We'll release() this semaphore when we exit so that another thread
280 can be started up.
David James89ece422014-01-09 18:51:58 -0800281 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700282 _FetchHelper docstring for details.
283 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500284 try:
285 for project in projects:
286 success = self._FetchHelper(opt, project, *args, **kwargs)
287 if not success and not opt.force_broken:
288 break
289 finally:
290 sem.release()
David James8d201162013-10-11 17:03:19 -0700291
Xin Li745be2e2019-06-03 11:24:30 -0700292 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
293 clone_filter):
David James8d201162013-10-11 17:03:19 -0700294 """Fetch git objects for a single project.
295
David Pursehousec1b86a22012-11-14 11:36:51 +0900296 Args:
297 opt: Program options returned from optparse. See _Options().
298 project: Project object for the project to fetch.
299 lock: Lock for accessing objects that are shared amongst multiple
300 _FetchHelper() threads.
301 fetched: set object that we will add project.gitdir to when we're done
302 (with our lock held).
303 pm: Instance of a Project object. We will call pm.update() (with our
304 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900305 err_event: We'll set this event in the case of an error (after printing
306 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700307 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700308
309 Returns:
310 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900311 """
312 # We'll set to true once we've locked the lock.
313 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700314
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530315 if not opt.quiet:
316 print('Fetching project %s' % project.name)
317
David Pursehousec1b86a22012-11-14 11:36:51 +0900318 # Encapsulate everything in a try/except/finally so that:
319 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900320 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700321 start = time.time()
322 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900323 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700324 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900325 success = project.Sync_NetworkHalf(
326 quiet=opt.quiet,
327 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700328 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700329 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900330 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
David Pursehouse74cfd272015-10-14 10:50:15 +0900331 optimized_fetch=opt.optimized_fetch,
Xin Li745be2e2019-06-03 11:24:30 -0700332 prune=opt.prune,
333 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900334 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700335
David Pursehousec1b86a22012-11-14 11:36:51 +0900336 # Lock around all the rest of the code, since printing, updating a set
337 # and Progress.update() are not thread safe.
338 lock.acquire()
339 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700340
David Pursehousec1b86a22012-11-14 11:36:51 +0900341 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800342 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700343 print('error: Cannot fetch %s from %s'
344 % (project.name, project.remote.url),
345 file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900346 if opt.force_broken:
347 print('warn: --force-broken, continuing to sync',
348 file=sys.stderr)
349 else:
350 raise _FetchError()
Roy Lee18afd7f2010-05-09 04:32:08 +0800351
David Pursehousec1b86a22012-11-14 11:36:51 +0900352 fetched.add(project.gitdir)
353 pm.update()
354 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800355 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400356 except Exception as e:
357 print('error: Cannot fetch %s (%s: %s)' \
358 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900359 err_event.set()
360 raise
361 finally:
362 if did_lock:
363 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700364 finish = time.time()
365 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
366 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800367
David James8d201162013-10-11 17:03:19 -0700368 return success
369
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700370 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700371 fetched = set()
David James89ece422014-01-09 18:51:58 -0800372 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200373 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200374 print_newline=not(opt.quiet),
375 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800376
David James89ece422014-01-09 18:51:58 -0800377 objdir_project_map = dict()
378 for project in projects:
379 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700380
David James89ece422014-01-09 18:51:58 -0800381 threads = set()
382 sem = _threading.Semaphore(self.jobs)
383 err_event = _threading.Event()
384 for project_list in objdir_project_map.values():
385 # Check for any errors before running any more tasks.
386 # ...we'll let existing threads finish, though.
387 if err_event.isSet() and not opt.force_broken:
388 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700389
David James89ece422014-01-09 18:51:58 -0800390 sem.acquire()
391 kwargs = dict(opt=opt,
392 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500393 sem=sem,
David James89ece422014-01-09 18:51:58 -0800394 lock=lock,
395 fetched=fetched,
396 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700397 err_event=err_event,
398 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800399 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700400 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800401 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200402 # Ensure that Ctrl-C will not freeze the repo process.
403 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800404 threads.add(t)
405 t.start()
David James89ece422014-01-09 18:51:58 -0800406 else:
407 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800408
David James89ece422014-01-09 18:51:58 -0800409 for t in threads:
410 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800411
David James89ece422014-01-09 18:51:58 -0800412 # If we saw an error, exit with code 1 so that other scripts can check.
Nicolas Cornu8419ab22017-06-16 12:09:06 +0200413 if err_event.isSet() and not opt.force_broken:
David James89ece422014-01-09 18:51:58 -0800414 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
415 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700416
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700417 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700418 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700419
Julien Campergue335f5ef2013-10-16 11:02:35 +0200420 if not self.manifest.IsArchive:
421 self._GCProjects(projects)
422
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700423 return fetched
424
Xin Li745be2e2019-06-03 11:24:30 -0700425 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
426 """Main function of the fetch threads.
427
428 Delegates most of the work to _CheckoutOne.
429
430 Args:
431 opt: Program options returned from optparse. See _Options().
432 projects: Projects to fetch.
433 sem: We'll release() this semaphore when we exit so that another thread
434 can be started up.
435 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
436 _CheckoutOne docstring for details.
437 """
438 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400439 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700440 finally:
441 sem.release()
442
443 def _CheckoutOne(self, opt, project, lock, pm, err_event):
444 """Checkout work tree for one project
445
446 Args:
447 opt: Program options returned from optparse. See _Options().
448 project: Project object for the project to checkout.
449 lock: Lock for accessing objects that are shared amongst multiple
450 _CheckoutWorker() threads.
451 pm: Instance of a Project object. We will call pm.update() (with our
452 lock held).
453 err_event: We'll set this event in the case of an error (after printing
454 out info about the error).
455
456 Returns:
457 Whether the fetch was successful.
458 """
459 # We'll set to true once we've locked the lock.
460 did_lock = False
461
462 if not opt.quiet:
463 print('Checking out project %s' % project.name)
464
465 # Encapsulate everything in a try/except/finally so that:
466 # - We always set err_event in the case of an exception.
467 # - We always make sure we unlock the lock if we locked it.
468 start = time.time()
469 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
470 detach_head=opt.detach_head)
471 success = False
472 try:
473 try:
474 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
475 success = syncbuf.Finish()
476
477 # Lock around all the rest of the code, since printing, updating a set
478 # and Progress.update() are not thread safe.
479 lock.acquire()
480 did_lock = True
481
482 if not success:
483 err_event.set()
484 print('error: Cannot checkout %s' % (project.name),
485 file=sys.stderr)
486 raise _CheckoutError()
487
488 pm.update()
489 except _CheckoutError:
490 pass
491 except Exception as e:
492 print('error: Cannot checkout %s: %s: %s' %
493 (project.name, type(e).__name__, str(e)),
494 file=sys.stderr)
495 err_event.set()
496 raise
497 finally:
498 if did_lock:
499 lock.release()
500 finish = time.time()
501 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
502 start, finish, success)
503
504 return success
505
506 def _Checkout(self, all_projects, opt):
507 """Checkout projects listed in all_projects
508
509 Args:
510 all_projects: List of all projects that should be checked out.
511 opt: Program options returned from optparse. See _Options().
512 """
513
514 # Perform checkouts in multiple threads when we are using partial clone.
515 # Without partial clone, all needed git objects are already downloaded,
516 # in this situation it's better to use only one process because the checkout
517 # would be mostly disk I/O; with partial clone, the objects are only
518 # downloaded when demanded (at checkout time), which is similar to the
519 # Sync_NetworkHalf case and parallelism would be helpful.
520 if self.manifest.CloneFilter:
521 syncjobs = self.jobs
522 else:
523 syncjobs = 1
524
525 lock = _threading.Lock()
526 pm = Progress('Syncing work tree', len(all_projects))
527
528 threads = set()
529 sem = _threading.Semaphore(syncjobs)
530 err_event = _threading.Event()
531
532 for project in all_projects:
533 # Check for any errors before running any more tasks.
534 # ...we'll let existing threads finish, though.
535 if err_event.isSet() and not opt.force_broken:
536 break
537
538 sem.acquire()
539 if project.worktree:
540 kwargs = dict(opt=opt,
541 sem=sem,
542 project=project,
543 lock=lock,
544 pm=pm,
545 err_event=err_event)
546 if syncjobs > 1:
547 t = _threading.Thread(target=self._CheckoutWorker,
548 kwargs=kwargs)
549 # Ensure that Ctrl-C will not freeze the repo process.
550 t.daemon = True
551 threads.add(t)
552 t.start()
553 else:
554 self._CheckoutWorker(**kwargs)
555
556 for t in threads:
557 t.join()
558
559 pm.end()
560 # If we saw an error, exit with code 1 so that other scripts can check.
561 if err_event.isSet():
562 print('\nerror: Exited sync due to checkout errors', file=sys.stderr)
563 sys.exit(1)
564
Dave Borowitz18857212012-10-23 17:02:59 -0700565 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700566 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700567 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700568 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
569 print('Shared project %s found, disabling pruning.' % project.name)
570 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
571 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700572
Dave Borowitze2152672012-10-31 12:24:38 -0700573 has_dash_c = git_require((1, 7, 2))
574 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700575 cpu_count = multiprocessing.cpu_count()
576 else:
577 cpu_count = 1
578 jobs = min(self.jobs, cpu_count)
579
580 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700581 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700582 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700583 return
584
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400585 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700586
587 threads = set()
588 sem = _threading.Semaphore(jobs)
589 err_event = _threading.Event()
590
David James8d201162013-10-11 17:03:19 -0700591 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700592 try:
593 try:
David James8d201162013-10-11 17:03:19 -0700594 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700595 except GitError:
596 err_event.set()
597 except:
598 err_event.set()
599 raise
600 finally:
601 sem.release()
602
Gabe Black2ff30292014-10-09 17:54:35 -0700603 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700604 if err_event.isSet():
605 break
606 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700607 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700608 t.daemon = True
609 threads.add(t)
610 t.start()
611
612 for t in threads:
613 t.join()
614
615 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700616 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700617 sys.exit(1)
618
Tim Kilbourn07669002013-03-08 15:02:49 -0800619 def _ReloadManifest(self, manifest_name=None):
620 if manifest_name:
621 # Override calls _Unload already
622 self.manifest.Override(manifest_name)
623 else:
624 self.manifest._Unload()
625
Dan Willemsen43507912016-09-01 16:26:02 -0700626 def _DeleteProject(self, path):
627 print('Deleting obsolete path %s' % path, file=sys.stderr)
628
629 # Delete the .git directory first, so we're less likely to have a partially
630 # working git repository around. There shouldn't be any git projects here,
631 # so rmtree works.
632 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -0700633 platform_utils.rmtree(os.path.join(path, '.git'))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700634 except OSError as e:
635 print('Failed to remove %s (%s)' % (os.path.join(path, '.git'), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700636 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
637 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400638 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700639
640 # Delete everything under the worktree, except for directories that contain
641 # another git project
642 dirs_to_remove = []
643 failed = False
Renaud Paquaybed8b622018-09-27 10:46:58 -0700644 for root, dirs, files in platform_utils.walk(path):
Dan Willemsen43507912016-09-01 16:26:02 -0700645 for f in files:
646 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800647 platform_utils.remove(os.path.join(root, f))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700648 except OSError as e:
649 print('Failed to remove %s (%s)' % (os.path.join(root, f), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700650 failed = True
651 dirs[:] = [d for d in dirs
652 if not os.path.lexists(os.path.join(root, d, '.git'))]
653 dirs_to_remove += [os.path.join(root, d) for d in dirs
654 if os.path.join(root, d) not in dirs_to_remove]
655 for d in reversed(dirs_to_remove):
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700656 if platform_utils.islink(d):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700657 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800658 platform_utils.remove(d)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700659 except OSError as e:
660 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700661 failed = True
Renaud Paquaybed8b622018-09-27 10:46:58 -0700662 elif len(platform_utils.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700663 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700664 platform_utils.rmdir(d)
665 except OSError as e:
666 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700667 failed = True
668 continue
669 if failed:
670 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
671 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400672 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700673
674 # Try deleting parent dirs if they are empty
675 project_dir = path
676 while project_dir != self.manifest.topdir:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700677 if len(platform_utils.listdir(project_dir)) == 0:
678 platform_utils.rmdir(project_dir)
Dan Willemsen43507912016-09-01 16:26:02 -0700679 else:
680 break
681 project_dir = os.path.dirname(project_dir)
682
683 return 0
684
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500685 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700686 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700687 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700688 if project.relpath:
689 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700690 file_name = 'project.list'
691 file_path = os.path.join(self.manifest.repodir, file_name)
692 old_project_paths = []
693
694 if os.path.exists(file_path):
695 fd = open(file_path, 'r')
696 try:
697 old_project_paths = fd.read().split('\n')
698 finally:
699 fd.close()
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800700 # In reversed order, so subfolders are deleted before parent folder.
701 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700702 if not path:
703 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700704 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900705 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700706 gitdir = os.path.join(self.manifest.topdir, path, '.git')
707 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900708 project = Project(
709 manifest = self.manifest,
710 name = path,
711 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700712 gitdir = gitdir,
713 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900714 worktree = os.path.join(self.manifest.topdir, path),
715 relpath = path,
716 revisionExpr = 'HEAD',
717 revisionId = None,
718 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400719
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500720 if project.IsDirty() and opt.force_remove_dirty:
721 print('WARNING: Removing dirty project "%s": uncommitted changes '
722 'erased' % project.relpath, file=sys.stderr)
723 self._DeleteProject(project.worktree)
724 elif project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900725 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900726 'are present' % project.relpath, file=sys.stderr)
727 print(' commit changes, then run sync again',
728 file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400729 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700730 elif self._DeleteProject(project.worktree):
Mike Frysingera850ca22019-08-07 17:19:24 -0400731 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700732
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700733 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700734 fd = open(file_path, 'w')
735 try:
736 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700737 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700738 finally:
739 fd.close()
740 return 0
741
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700742 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800743 if opt.jobs:
744 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700745 if self.jobs > 1:
746 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400747 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700748
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700749 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700750 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700751 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700752 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700753 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700754 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500755 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700756 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500757 sys.exit(1)
758 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700759 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500760 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900761 if opt.manifest_server_username or opt.manifest_server_password:
762 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700763 print('error: -u and -p may only be combined with -s or -t',
764 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900765 sys.exit(1)
766 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700767 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900768 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500769
770 if opt.manifest_name:
771 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700772
Chirayu Desaia892b102013-06-11 14:18:46 +0530773 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900774 smart_sync_manifest_name = "smart_sync_override.xml"
775 smart_sync_manifest_path = os.path.join(
776 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530777
Victor Boivie08c880d2011-04-19 10:32:52 +0200778 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700779 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900780 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700781 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700782 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900783
784 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900785 if not opt.quiet:
786 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900787
David Pursehouse86d973d2012-08-24 10:21:02 +0900788 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900789 username = None
790 password = None
791 if opt.manifest_server_username and opt.manifest_server_password:
792 username = opt.manifest_server_username
793 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900794 else:
795 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900796 info = netrc.netrc()
797 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900798 # .netrc file does not exist or could not be opened
799 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900800 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900801 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530802 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900803 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900804 auth = info.authenticators(parse_result.hostname)
805 if auth:
806 username, _account, password = auth
807 else:
808 print('No credentials found for %s in .netrc'
809 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700810 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700811 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900812
813 if (username and password):
814 manifest_server = manifest_server.replace('://', '://%s:%s@' %
815 (username, password),
816 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900817
Dan Willemsen0745bb22015-08-17 13:41:45 -0700818 transport = PersistentTransport(manifest_server)
819 if manifest_server.startswith('persistent-'):
820 manifest_server = manifest_server[len('persistent-'):]
821
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700822 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700823 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200824 if opt.smart_sync:
825 p = self.manifest.manifestProject
826 b = p.GetBranch(p.CurrentBranch)
827 branch = b.merge
828 if branch.startswith(R_HEADS):
829 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700830
Victor Boivie08c880d2011-04-19 10:32:52 +0200831 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700832 if 'SYNC_TARGET' in env:
833 target = env['SYNC_TARGET']
834 [success, manifest_str] = server.GetApprovedManifest(branch, target)
835 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200836 target = '%s-%s' % (env['TARGET_PRODUCT'],
837 env['TARGET_BUILD_VARIANT'])
838 [success, manifest_str] = server.GetApprovedManifest(branch, target)
839 else:
840 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700841 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200842 assert(opt.smart_tag)
843 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700844
845 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900846 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700847 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900848 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700849 try:
850 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700851 finally:
852 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900853 except IOError as e:
854 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900855 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700856 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700857 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100858 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700859 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900860 print('error: manifest server RPC call failed: %s' %
861 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700862 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530863 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700864 print('error: cannot connect to manifest server %s:\n%s'
865 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900866 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530867 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700868 print('error: cannot connect to manifest server %s:\n%d %s'
869 % (self.manifest.manifest_server, e.errcode, e.errmsg),
870 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700871 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900872 else: # Not smart sync or smart tag mode
873 if os.path.isfile(smart_sync_manifest_path):
874 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800875 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900876 except OSError as e:
877 print('error: failed to remove existing smart sync override manifest: %s' %
878 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700879
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700880 rp = self.manifest.repoProject
881 rp.PreSync()
882
883 mp = self.manifest.manifestProject
884 mp.PreSync()
885
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800886 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700887 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800888
Nico Sallembien9bb18162009-12-07 15:38:01 -0800889 if not opt.local_only:
David Rileye0684ad2017-04-05 00:02:59 -0700890 start = time.time()
891 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
892 current_branch_only=opt.current_branch_only,
893 no_tags=opt.no_tags,
894 optimized_fetch=opt.optimized_fetch,
Xin Li745be2e2019-06-03 11:24:30 -0700895 submodules=self.manifest.HasSubmodules,
896 clone_filter=self.manifest.CloneFilter)
David Rileye0684ad2017-04-05 00:02:59 -0700897 finish = time.time()
898 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
899 start, finish, success)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800900
901 if mp.HasChanges:
902 syncbuf = SyncBuffer(mp.config)
David Rileye0684ad2017-04-05 00:02:59 -0700903 start = time.time()
Martin Kellye4e94d22017-03-21 16:05:12 -0700904 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
David Rileye0684ad2017-04-05 00:02:59 -0700905 clean = syncbuf.Finish()
906 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
907 start, time.time(), clean)
908 if not clean:
Nico Sallembien9bb18162009-12-07 15:38:01 -0800909 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100910 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700911 if opt.jobs is None:
912 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700913
Simran Basib9a1b732015-08-20 12:19:28 -0700914 if self.gitc_manifest:
915 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700916 missing_ok=True)
917 gitc_projects = []
918 opened_projects = []
919 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700920 if project.relpath in self.gitc_manifest.paths and \
921 self.gitc_manifest.paths[project.relpath].old_revision:
922 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700923 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700924 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700925
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700926 if not args:
927 gitc_projects = None
928
929 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700930 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700931 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
932 if manifest_name:
933 manifest.Override(manifest_name)
934 else:
935 manifest.Override(self.manifest.manifestFile)
936 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
937 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700938 gitc_projects)
939 print('GITC client successfully synced.')
940
941 # The opened projects need to be synced as normal, therefore we
942 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700943 # TODO: make this more reliable -- if there's a project name/path overlap,
944 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900945 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
946 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700947 if not args:
948 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800949 all_projects = self.GetProjects(args,
950 missing_ok=True,
951 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952
Dave Borowitz67700e92012-10-23 15:00:54 -0700953 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700954 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700955 to_fetch = []
956 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700957 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700958 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900959 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700960 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700961
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800962 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700963 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700964 if opt.network_only:
965 # bail out now; the rest touches the working tree
966 return
967
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800968 # Iteratively fetch missing and/or nested unregistered submodules
969 previously_missing_set = set()
970 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100971 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800972 all_projects = self.GetProjects(args,
973 missing_ok=True,
974 submodules_ok=opt.fetch_submodules)
975 missing = []
976 for project in all_projects:
977 if project.gitdir not in fetched:
978 missing.append(project)
979 if not missing:
980 break
981 # Stop us from non-stopped fetching actually-missing repos: If set of
982 # missing repos has not been changed from last fetch, we break.
983 missing_set = set(p.name for p in missing)
984 if previously_missing_set == missing_set:
985 break
986 previously_missing_set = missing_set
987 fetched.update(self._Fetch(missing, opt))
988
Julien Campergue335f5ef2013-10-16 11:02:35 +0200989 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700990 # bail out now, we have no working tree
991 return
992
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500993 if self.UpdateProjectList(opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700994 sys.exit(1)
995
Xin Li745be2e2019-06-03 11:24:30 -0700996 self._Checkout(all_projects, opt)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700997
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700998 # If there's a notice that's supposed to print at the end of the sync, print
999 # it now...
1000 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001001 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001002
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001003def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001004 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001005 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001006 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001007 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001008 if project.Exists:
1009 project.PostRepoUpgrade()
1010
1011def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
1012 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001013 print('info: A new version of repo is available', file=sys.stderr)
1014 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001015 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001016 syncbuf = SyncBuffer(rp.config)
1017 rp.Sync_LocalHalf(syncbuf)
1018 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001019 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001020 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001021 raise RepoChangedException(['--repo-upgraded'])
1022 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001023 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001024 else:
1025 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001026 print('repo version %s is current' % rp.work_git.describe(HEAD),
1027 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001028
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001029def _VerifyTag(project):
1030 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1031 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001032 print('warning: GnuPG was not available during last "repo init"\n'
1033 'warning: Cannot automatically authenticate repo."""',
1034 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035 return True
1036
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001037 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001038 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001039 except GitError:
1040 cur = None
1041
1042 if not cur \
1043 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001044 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001045 if rev.startswith(R_HEADS):
1046 rev = rev[len(R_HEADS):]
1047
Sarah Owenscecd1d82012-11-01 22:59:27 -07001048 print(file=sys.stderr)
1049 print("warning: project '%s' branch '%s' is not signed"
1050 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001051 return False
1052
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001053 env = os.environ.copy()
1054 env['GIT_DIR'] = project.gitdir.encode()
1055 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001056
1057 cmd = [GIT, 'tag', '-v', cur]
1058 proc = subprocess.Popen(cmd,
1059 stdout = subprocess.PIPE,
1060 stderr = subprocess.PIPE,
1061 env = env)
1062 out = proc.stdout.read()
1063 proc.stdout.close()
1064
1065 err = proc.stderr.read()
1066 proc.stderr.close()
1067
1068 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001069 print(file=sys.stderr)
1070 print(out, file=sys.stderr)
1071 print(err, file=sys.stderr)
1072 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001073 return False
1074 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001075
David Rileye0684ad2017-04-05 00:02:59 -07001076
Dave Borowitz67700e92012-10-23 15:00:54 -07001077class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001078 _ALPHA = 0.5
1079
Dave Borowitz67700e92012-10-23 15:00:54 -07001080 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001081 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001082 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001083 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001084
1085 def Get(self, project):
1086 self._Load()
1087 return self._times.get(project.name, _ONE_DAY_S)
1088
1089 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001090 self._Load()
1091 name = project.name
1092 old = self._times.get(name, t)
1093 self._seen.add(name)
1094 a = self._ALPHA
1095 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001096
1097 def _Load(self):
1098 if self._times is None:
1099 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001100 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -07001101 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001102 self._times = json.load(f)
1103 finally:
1104 f.close()
1105 except (IOError, ValueError):
1106 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001107 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001108 except OSError:
1109 pass
1110 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001111
1112 def Save(self):
1113 if self._times is None:
1114 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001115
1116 to_delete = []
1117 for name in self._times:
1118 if name not in self._seen:
1119 to_delete.append(name)
1120 for name in to_delete:
1121 del self._times[name]
1122
Dave Borowitz67700e92012-10-23 15:00:54 -07001123 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001124 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -07001125 try:
Anthony King85b24ac2014-05-06 15:57:48 +01001126 json.dump(self._times, f, indent=2)
1127 finally:
1128 f.close()
1129 except (IOError, TypeError):
1130 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001131 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001132 except OSError:
1133 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001134
1135# This is a replacement for xmlrpc.client.Transport using urllib2
1136# and supporting persistent-http[s]. It cannot change hosts from
1137# request to request like the normal transport, the real url
1138# is passed during initialization.
1139class PersistentTransport(xmlrpc.client.Transport):
1140 def __init__(self, orig_host):
1141 self.orig_host = orig_host
1142
1143 def request(self, host, handler, request_body, verbose=False):
1144 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1145 # Python doesn't understand cookies with the #HttpOnly_ prefix
1146 # Since we're only using them for HTTP, copy the file temporarily,
1147 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001148 if cookiefile:
1149 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001150 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001151 try:
1152 with open(cookiefile) as f:
1153 for line in f:
1154 if line.startswith("#HttpOnly_"):
1155 line = line[len("#HttpOnly_"):]
1156 tmpcookiefile.write(line)
1157 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001158
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001159 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001160 try:
1161 cookiejar.load()
1162 except cookielib.LoadError:
1163 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001164 finally:
1165 tmpcookiefile.close()
1166 else:
1167 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001168
1169 proxyhandler = urllib.request.ProxyHandler
1170 if proxy:
1171 proxyhandler = urllib.request.ProxyHandler({
1172 "http": proxy,
1173 "https": proxy })
1174
1175 opener = urllib.request.build_opener(
1176 urllib.request.HTTPCookieProcessor(cookiejar),
1177 proxyhandler)
1178
1179 url = urllib.parse.urljoin(self.orig_host, handler)
1180 parse_results = urllib.parse.urlparse(url)
1181
1182 scheme = parse_results.scheme
1183 if scheme == 'persistent-http':
1184 scheme = 'http'
1185 if scheme == 'persistent-https':
1186 # If we're proxying through persistent-https, use http. The
1187 # proxy itself will do the https.
1188 if proxy:
1189 scheme = 'http'
1190 else:
1191 scheme = 'https'
1192
1193 # Parse out any authentication information using the base class
1194 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1195
1196 url = urllib.parse.urlunparse((
1197 scheme,
1198 host,
1199 parse_results.path,
1200 parse_results.params,
1201 parse_results.query,
1202 parse_results.fragment))
1203
1204 request = urllib.request.Request(url, request_body)
1205 if extra_headers is not None:
1206 for (name, header) in extra_headers:
1207 request.add_header(name, header)
1208 request.add_header('Content-Type', 'text/xml')
1209 try:
1210 response = opener.open(request)
1211 except urllib.error.HTTPError as e:
1212 if e.code == 501:
1213 # We may have been redirected through a login process
1214 # but our POST turned into a GET. Retry.
1215 response = opener.open(request)
1216 else:
1217 raise
1218
1219 p, u = xmlrpc.client.getparser()
1220 while 1:
1221 data = response.read(1024)
1222 if not data:
1223 break
1224 p.feed(data)
1225 p.close()
1226 return u.close()
1227
1228 def close(self):
1229 pass
1230