blob: 2973a16ec134cac6e762c43f49a42d654fdbc3d9 [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',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200202 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400203 help='obsolete option (to be deleted in the future)')
204 p.add_option('--fail-fast',
205 dest='fail_fast', action='store_true',
206 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700207 p.add_option('--force-sync',
208 dest='force_sync', action='store_true',
209 help="overwrite an existing git directory if it needs to "
210 "point to a different object directory. WARNING: this "
211 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500212 p.add_option('--force-remove-dirty',
213 dest='force_remove_dirty', action='store_true',
214 help="force remove projects with uncommitted modifications if "
215 "projects no longer exist in the manifest. "
216 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700218 dest='local_only', action='store_true',
219 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900220 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700221 dest='network_only', action='store_true',
222 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900223 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700224 dest='detach_head', action='store_true',
225 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900226 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700227 dest='current_branch_only', action='store_true',
228 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900229 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700230 dest='quiet', action='store_true',
231 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900232 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800233 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700234 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500235 p.add_option('-m', '--manifest-name',
236 dest='manifest_name',
237 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700238 p.add_option('--no-clone-bundle',
239 dest='no_clone_bundle', action='store_true',
240 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800241 p.add_option('-u', '--manifest-server-username', action='store',
242 dest='manifest_server_username',
243 help='username to authenticate with the manifest server')
244 p.add_option('-p', '--manifest-server-password', action='store',
245 dest='manifest_server_password',
246 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800247 p.add_option('--fetch-submodules',
248 dest='fetch_submodules', action='store_true',
249 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700250 p.add_option('--no-tags',
251 dest='no_tags', action='store_true',
252 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900253 p.add_option('--optimized-fetch',
254 dest='optimized_fetch', action='store_true',
255 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900256 p.add_option('--prune', dest='prune', action='store_true',
257 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700258 if show_smart:
259 p.add_option('-s', '--smart-sync',
260 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900261 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200262 p.add_option('-t', '--smart-tag',
263 dest='smart_tag', action='store',
264 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700265
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700266 g = p.add_option_group('repo Version options')
267 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268 dest='no_repo_verify', action='store_true',
269 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700270 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800271 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700272 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700273
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500274 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700275 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800276
David James8d201162013-10-11 17:03:19 -0700277 Delegates most of the work to _FetchHelper.
278
279 Args:
280 opt: Program options returned from optparse. See _Options().
281 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500282 sem: We'll release() this semaphore when we exit so that another thread
283 can be started up.
David James89ece422014-01-09 18:51:58 -0800284 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700285 _FetchHelper docstring for details.
286 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500287 try:
288 for project in projects:
289 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400290 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500291 break
292 finally:
293 sem.release()
David James8d201162013-10-11 17:03:19 -0700294
Xin Li745be2e2019-06-03 11:24:30 -0700295 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
296 clone_filter):
David James8d201162013-10-11 17:03:19 -0700297 """Fetch git objects for a single project.
298
David Pursehousec1b86a22012-11-14 11:36:51 +0900299 Args:
300 opt: Program options returned from optparse. See _Options().
301 project: Project object for the project to fetch.
302 lock: Lock for accessing objects that are shared amongst multiple
303 _FetchHelper() threads.
304 fetched: set object that we will add project.gitdir to when we're done
305 (with our lock held).
306 pm: Instance of a Project object. We will call pm.update() (with our
307 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900308 err_event: We'll set this event in the case of an error (after printing
309 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700310 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700311
312 Returns:
313 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900314 """
315 # We'll set to true once we've locked the lock.
316 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700317
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)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400346 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900347 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700348
David Pursehousec1b86a22012-11-14 11:36:51 +0900349 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400350 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900351 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800352 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400353 except Exception as e:
354 print('error: Cannot fetch %s (%s: %s)' \
355 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900356 err_event.set()
357 raise
358 finally:
359 if did_lock:
360 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700361 finish = time.time()
362 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
363 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800364
David James8d201162013-10-11 17:03:19 -0700365 return success
366
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700367 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700368 fetched = set()
David James89ece422014-01-09 18:51:58 -0800369 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200370 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200371 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800372
David James89ece422014-01-09 18:51:58 -0800373 objdir_project_map = dict()
374 for project in projects:
375 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700376
David James89ece422014-01-09 18:51:58 -0800377 threads = set()
378 sem = _threading.Semaphore(self.jobs)
379 err_event = _threading.Event()
380 for project_list in objdir_project_map.values():
381 # Check for any errors before running any more tasks.
382 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400383 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800384 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700385
David James89ece422014-01-09 18:51:58 -0800386 sem.acquire()
387 kwargs = dict(opt=opt,
388 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500389 sem=sem,
David James89ece422014-01-09 18:51:58 -0800390 lock=lock,
391 fetched=fetched,
392 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700393 err_event=err_event,
394 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800395 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700396 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800397 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200398 # Ensure that Ctrl-C will not freeze the repo process.
399 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800400 threads.add(t)
401 t.start()
David James89ece422014-01-09 18:51:58 -0800402 else:
403 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800404
David James89ece422014-01-09 18:51:58 -0800405 for t in threads:
406 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800407
David James89ece422014-01-09 18:51:58 -0800408 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400409 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800410 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
411 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700412
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700413 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700414 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700415
Julien Campergue335f5ef2013-10-16 11:02:35 +0200416 if not self.manifest.IsArchive:
417 self._GCProjects(projects)
418
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700419 return fetched
420
Xin Li745be2e2019-06-03 11:24:30 -0700421 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
422 """Main function of the fetch threads.
423
424 Delegates most of the work to _CheckoutOne.
425
426 Args:
427 opt: Program options returned from optparse. See _Options().
428 projects: Projects to fetch.
429 sem: We'll release() this semaphore when we exit so that another thread
430 can be started up.
431 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
432 _CheckoutOne docstring for details.
433 """
434 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400435 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700436 finally:
437 sem.release()
438
Vadim Bendeburydff91942019-11-06 11:05:00 -0800439 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700440 """Checkout work tree for one project
441
442 Args:
443 opt: Program options returned from optparse. See _Options().
444 project: Project object for the project to checkout.
445 lock: Lock for accessing objects that are shared amongst multiple
446 _CheckoutWorker() threads.
447 pm: Instance of a Project object. We will call pm.update() (with our
448 lock held).
449 err_event: We'll set this event in the case of an error (after printing
450 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800451 err_results: A list of strings, paths to git repos where checkout
452 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700453
454 Returns:
455 Whether the fetch was successful.
456 """
457 # We'll set to true once we've locked the lock.
458 did_lock = False
459
Xin Li745be2e2019-06-03 11:24:30 -0700460 # Encapsulate everything in a try/except/finally so that:
461 # - We always set err_event in the case of an exception.
462 # - We always make sure we unlock the lock if we locked it.
463 start = time.time()
464 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
465 detach_head=opt.detach_head)
466 success = False
467 try:
468 try:
469 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700470
471 # Lock around all the rest of the code, since printing, updating a set
472 # and Progress.update() are not thread safe.
473 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400474 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700475 did_lock = True
476
477 if not success:
478 err_event.set()
479 print('error: Cannot checkout %s' % (project.name),
480 file=sys.stderr)
481 raise _CheckoutError()
482
Mike Frysinger3538dd22019-08-26 15:32:06 -0400483 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700484 except _CheckoutError:
485 pass
486 except Exception as e:
487 print('error: Cannot checkout %s: %s: %s' %
488 (project.name, type(e).__name__, str(e)),
489 file=sys.stderr)
490 err_event.set()
491 raise
492 finally:
493 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800494 if not success:
495 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700496 lock.release()
497 finish = time.time()
498 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
499 start, finish, success)
500
501 return success
502
503 def _Checkout(self, all_projects, opt):
504 """Checkout projects listed in all_projects
505
506 Args:
507 all_projects: List of all projects that should be checked out.
508 opt: Program options returned from optparse. See _Options().
509 """
510
511 # Perform checkouts in multiple threads when we are using partial clone.
512 # Without partial clone, all needed git objects are already downloaded,
513 # in this situation it's better to use only one process because the checkout
514 # would be mostly disk I/O; with partial clone, the objects are only
515 # downloaded when demanded (at checkout time), which is similar to the
516 # Sync_NetworkHalf case and parallelism would be helpful.
517 if self.manifest.CloneFilter:
518 syncjobs = self.jobs
519 else:
520 syncjobs = 1
521
522 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400523 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700524
525 threads = set()
526 sem = _threading.Semaphore(syncjobs)
527 err_event = _threading.Event()
Vadim Bendeburydff91942019-11-06 11:05:00 -0800528 err_results = []
Xin Li745be2e2019-06-03 11:24:30 -0700529
530 for project in all_projects:
531 # Check for any errors before running any more tasks.
532 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400533 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700534 break
535
536 sem.acquire()
537 if project.worktree:
538 kwargs = dict(opt=opt,
539 sem=sem,
540 project=project,
541 lock=lock,
542 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800543 err_event=err_event,
544 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700545 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)
Vadim Bendeburydff91942019-11-06 11:05:00 -0800562 if err_results:
563 print('Failing repos:\n%s' % '\n'.join(err_results),
564 file=sys.stderr)
Xin Li745be2e2019-06-03 11:24:30 -0700565 sys.exit(1)
566
Dave Borowitz18857212012-10-23 17:02:59 -0700567 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700568 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700569 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700570 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
571 print('Shared project %s found, disabling pruning.' % project.name)
572 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
573 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700574
Dave Borowitze2152672012-10-31 12:24:38 -0700575 has_dash_c = git_require((1, 7, 2))
576 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700577 cpu_count = multiprocessing.cpu_count()
578 else:
579 cpu_count = 1
580 jobs = min(self.jobs, cpu_count)
581
582 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700583 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700584 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700585 return
586
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400587 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700588
589 threads = set()
590 sem = _threading.Semaphore(jobs)
591 err_event = _threading.Event()
592
David James8d201162013-10-11 17:03:19 -0700593 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700594 try:
595 try:
David James8d201162013-10-11 17:03:19 -0700596 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700597 except GitError:
598 err_event.set()
599 except:
600 err_event.set()
601 raise
602 finally:
603 sem.release()
604
Gabe Black2ff30292014-10-09 17:54:35 -0700605 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700606 if err_event.isSet():
607 break
608 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700609 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700610 t.daemon = True
611 threads.add(t)
612 t.start()
613
614 for t in threads:
615 t.join()
616
617 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700618 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700619 sys.exit(1)
620
Tim Kilbourn07669002013-03-08 15:02:49 -0800621 def _ReloadManifest(self, manifest_name=None):
622 if manifest_name:
623 # Override calls _Unload already
624 self.manifest.Override(manifest_name)
625 else:
626 self.manifest._Unload()
627
Dan Willemsen43507912016-09-01 16:26:02 -0700628 def _DeleteProject(self, path):
629 print('Deleting obsolete path %s' % path, file=sys.stderr)
630
631 # Delete the .git directory first, so we're less likely to have a partially
632 # working git repository around. There shouldn't be any git projects here,
633 # so rmtree works.
634 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -0700635 platform_utils.rmtree(os.path.join(path, '.git'))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700636 except OSError as e:
637 print('Failed to remove %s (%s)' % (os.path.join(path, '.git'), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700638 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
639 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400640 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700641
642 # Delete everything under the worktree, except for directories that contain
643 # another git project
644 dirs_to_remove = []
645 failed = False
Renaud Paquaybed8b622018-09-27 10:46:58 -0700646 for root, dirs, files in platform_utils.walk(path):
Dan Willemsen43507912016-09-01 16:26:02 -0700647 for f in files:
648 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800649 platform_utils.remove(os.path.join(root, f))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700650 except OSError as e:
651 print('Failed to remove %s (%s)' % (os.path.join(root, f), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700652 failed = True
653 dirs[:] = [d for d in dirs
654 if not os.path.lexists(os.path.join(root, d, '.git'))]
655 dirs_to_remove += [os.path.join(root, d) for d in dirs
656 if os.path.join(root, d) not in dirs_to_remove]
657 for d in reversed(dirs_to_remove):
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700658 if platform_utils.islink(d):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700659 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800660 platform_utils.remove(d)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700661 except OSError as e:
662 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700663 failed = True
Renaud Paquaybed8b622018-09-27 10:46:58 -0700664 elif len(platform_utils.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700665 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700666 platform_utils.rmdir(d)
667 except OSError as e:
668 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700669 failed = True
670 continue
671 if failed:
672 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
673 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400674 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700675
676 # Try deleting parent dirs if they are empty
677 project_dir = path
678 while project_dir != self.manifest.topdir:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700679 if len(platform_utils.listdir(project_dir)) == 0:
680 platform_utils.rmdir(project_dir)
Dan Willemsen43507912016-09-01 16:26:02 -0700681 else:
682 break
683 project_dir = os.path.dirname(project_dir)
684
685 return 0
686
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500687 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700688 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700689 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700690 if project.relpath:
691 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700692 file_name = 'project.list'
693 file_path = os.path.join(self.manifest.repodir, file_name)
694 old_project_paths = []
695
696 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500697 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700698 old_project_paths = fd.read().split('\n')
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()
Mike Frysinger3164d402019-11-11 05:40:22 -0500733 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700734 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700735 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700736 return 0
737
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400738 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
739 if not self.manifest.manifest_server:
740 print('error: cannot smart sync: no manifest server defined in '
741 'manifest', file=sys.stderr)
742 sys.exit(1)
743
744 manifest_server = self.manifest.manifest_server
745 if not opt.quiet:
746 print('Using manifest server %s' % manifest_server)
747
748 if not '@' in manifest_server:
749 username = None
750 password = None
751 if opt.manifest_server_username and opt.manifest_server_password:
752 username = opt.manifest_server_username
753 password = opt.manifest_server_password
754 else:
755 try:
756 info = netrc.netrc()
757 except IOError:
758 # .netrc file does not exist or could not be opened
759 pass
760 else:
761 try:
762 parse_result = urllib.parse.urlparse(manifest_server)
763 if parse_result.hostname:
764 auth = info.authenticators(parse_result.hostname)
765 if auth:
766 username, _account, password = auth
767 else:
768 print('No credentials found for %s in .netrc'
769 % parse_result.hostname, file=sys.stderr)
770 except netrc.NetrcParseError as e:
771 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
772
773 if (username and password):
774 manifest_server = manifest_server.replace('://', '://%s:%s@' %
775 (username, password),
776 1)
777
778 transport = PersistentTransport(manifest_server)
779 if manifest_server.startswith('persistent-'):
780 manifest_server = manifest_server[len('persistent-'):]
781
782 try:
783 server = xmlrpc.client.Server(manifest_server, transport=transport)
784 if opt.smart_sync:
785 p = self.manifest.manifestProject
786 b = p.GetBranch(p.CurrentBranch)
787 branch = b.merge
788 if branch.startswith(R_HEADS):
789 branch = branch[len(R_HEADS):]
790
791 env = os.environ.copy()
792 if 'SYNC_TARGET' in env:
793 target = env['SYNC_TARGET']
794 [success, manifest_str] = server.GetApprovedManifest(branch, target)
795 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
796 target = '%s-%s' % (env['TARGET_PRODUCT'],
797 env['TARGET_BUILD_VARIANT'])
798 [success, manifest_str] = server.GetApprovedManifest(branch, target)
799 else:
800 [success, manifest_str] = server.GetApprovedManifest(branch)
801 else:
802 assert(opt.smart_tag)
803 [success, manifest_str] = server.GetManifest(opt.smart_tag)
804
805 if success:
806 manifest_name = os.path.basename(smart_sync_manifest_path)
807 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500808 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400809 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400810 except IOError as e:
811 print('error: cannot write manifest to %s:\n%s'
812 % (smart_sync_manifest_path, e),
813 file=sys.stderr)
814 sys.exit(1)
815 self._ReloadManifest(manifest_name)
816 else:
817 print('error: manifest server RPC call failed: %s' %
818 manifest_str, file=sys.stderr)
819 sys.exit(1)
820 except (socket.error, IOError, xmlrpc.client.Fault) as e:
821 print('error: cannot connect to manifest server %s:\n%s'
822 % (self.manifest.manifest_server, e), file=sys.stderr)
823 sys.exit(1)
824 except xmlrpc.client.ProtocolError as e:
825 print('error: cannot connect to manifest server %s:\n%d %s'
826 % (self.manifest.manifest_server, e.errcode, e.errmsg),
827 file=sys.stderr)
828 sys.exit(1)
829
830 return manifest_name
831
Mike Frysingerfb527e32019-08-27 02:34:32 -0400832 def _UpdateManifestProject(self, opt, mp, manifest_name):
833 """Fetch & update the local manifest project."""
834 if not opt.local_only:
835 start = time.time()
836 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
837 current_branch_only=opt.current_branch_only,
838 no_tags=opt.no_tags,
839 optimized_fetch=opt.optimized_fetch,
840 submodules=self.manifest.HasSubmodules,
841 clone_filter=self.manifest.CloneFilter)
842 finish = time.time()
843 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
844 start, finish, success)
845
846 if mp.HasChanges:
847 syncbuf = SyncBuffer(mp.config)
848 start = time.time()
849 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
850 clean = syncbuf.Finish()
851 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
852 start, time.time(), clean)
853 if not clean:
854 sys.exit(1)
855 self._ReloadManifest(opt.manifest_name)
856 if opt.jobs is None:
857 self.jobs = self.manifest.default.sync_j
858
Mike Frysingerae6cb082019-08-27 01:10:59 -0400859 def ValidateOptions(self, opt, args):
860 if opt.force_broken:
861 print('warning: -f/--force-broken is now the default behavior, and the '
862 'options are deprecated', file=sys.stderr)
863 if opt.network_only and opt.detach_head:
864 self.OptionParser.error('cannot combine -n and -d')
865 if opt.network_only and opt.local_only:
866 self.OptionParser.error('cannot combine -n and -l')
867 if opt.manifest_name and opt.smart_sync:
868 self.OptionParser.error('cannot combine -m and -s')
869 if opt.manifest_name and opt.smart_tag:
870 self.OptionParser.error('cannot combine -m and -t')
871 if opt.manifest_server_username or opt.manifest_server_password:
872 if not (opt.smart_sync or opt.smart_tag):
873 self.OptionParser.error('-u and -p may only be combined with -s or -t')
874 if None in [opt.manifest_server_username, opt.manifest_server_password]:
875 self.OptionParser.error('both -u and -p must be given')
876
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700877 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800878 if opt.jobs:
879 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700880 if self.jobs > 1:
881 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400882 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700883
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500884 if opt.manifest_name:
885 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700886
Chirayu Desaia892b102013-06-11 14:18:46 +0530887 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900888 smart_sync_manifest_path = os.path.join(
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400889 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530890
Victor Boivie08c880d2011-04-19 10:32:52 +0200891 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400892 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
893 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900894 if os.path.isfile(smart_sync_manifest_path):
895 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800896 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900897 except OSError as e:
898 print('error: failed to remove existing smart sync override manifest: %s' %
899 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700900
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700901 rp = self.manifest.repoProject
902 rp.PreSync()
903
904 mp = self.manifest.manifestProject
905 mp.PreSync()
906
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800907 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700908 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800909
Mike Frysingerfb527e32019-08-27 02:34:32 -0400910 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700911
Simran Basib9a1b732015-08-20 12:19:28 -0700912 if self.gitc_manifest:
913 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700914 missing_ok=True)
915 gitc_projects = []
916 opened_projects = []
917 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700918 if project.relpath in self.gitc_manifest.paths and \
919 self.gitc_manifest.paths[project.relpath].old_revision:
920 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700921 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700922 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700923
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700924 if not args:
925 gitc_projects = None
926
927 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700928 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700929 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
930 if manifest_name:
931 manifest.Override(manifest_name)
932 else:
933 manifest.Override(self.manifest.manifestFile)
934 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
935 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700936 gitc_projects)
937 print('GITC client successfully synced.')
938
939 # The opened projects need to be synced as normal, therefore we
940 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700941 # TODO: make this more reliable -- if there's a project name/path overlap,
942 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900943 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
944 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700945 if not args:
946 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800947 all_projects = self.GetProjects(args,
948 missing_ok=True,
949 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700950
Dave Borowitz67700e92012-10-23 15:00:54 -0700951 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700952 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700953 to_fetch = []
954 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700955 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700956 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900957 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700958 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700959
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800960 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700961 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700962 if opt.network_only:
963 # bail out now; the rest touches the working tree
964 return
965
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800966 # Iteratively fetch missing and/or nested unregistered submodules
967 previously_missing_set = set()
968 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100969 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800970 all_projects = self.GetProjects(args,
971 missing_ok=True,
972 submodules_ok=opt.fetch_submodules)
973 missing = []
974 for project in all_projects:
975 if project.gitdir not in fetched:
976 missing.append(project)
977 if not missing:
978 break
979 # Stop us from non-stopped fetching actually-missing repos: If set of
980 # missing repos has not been changed from last fetch, we break.
981 missing_set = set(p.name for p in missing)
982 if previously_missing_set == missing_set:
983 break
984 previously_missing_set = missing_set
985 fetched.update(self._Fetch(missing, opt))
986
Julien Campergue335f5ef2013-10-16 11:02:35 +0200987 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700988 # bail out now, we have no working tree
989 return
990
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500991 if self.UpdateProjectList(opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700992 sys.exit(1)
993
Xin Li745be2e2019-06-03 11:24:30 -0700994 self._Checkout(all_projects, opt)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700995
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700996 # If there's a notice that's supposed to print at the end of the sync, print
997 # it now...
998 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700999 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001000
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001001def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001002 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001003 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001004 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001005 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001006 if project.Exists:
1007 project.PostRepoUpgrade()
1008
1009def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
1010 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001011 print('info: A new version of repo is available', file=sys.stderr)
1012 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001013 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001014 syncbuf = SyncBuffer(rp.config)
1015 rp.Sync_LocalHalf(syncbuf)
1016 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001017 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001018 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001019 raise RepoChangedException(['--repo-upgraded'])
1020 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001021 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001022 else:
1023 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001024 print('repo version %s is current' % rp.work_git.describe(HEAD),
1025 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001026
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001027def _VerifyTag(project):
1028 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1029 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001030 print('warning: GnuPG was not available during last "repo init"\n'
1031 'warning: Cannot automatically authenticate repo."""',
1032 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033 return True
1034
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001036 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001037 except GitError:
1038 cur = None
1039
1040 if not cur \
1041 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001042 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001043 if rev.startswith(R_HEADS):
1044 rev = rev[len(R_HEADS):]
1045
Sarah Owenscecd1d82012-11-01 22:59:27 -07001046 print(file=sys.stderr)
1047 print("warning: project '%s' branch '%s' is not signed"
1048 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001049 return False
1050
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001051 env = os.environ.copy()
1052 env['GIT_DIR'] = project.gitdir.encode()
1053 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001054
1055 cmd = [GIT, 'tag', '-v', cur]
1056 proc = subprocess.Popen(cmd,
1057 stdout = subprocess.PIPE,
1058 stderr = subprocess.PIPE,
1059 env = env)
1060 out = proc.stdout.read()
1061 proc.stdout.close()
1062
1063 err = proc.stderr.read()
1064 proc.stderr.close()
1065
1066 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001067 print(file=sys.stderr)
1068 print(out, file=sys.stderr)
1069 print(err, file=sys.stderr)
1070 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071 return False
1072 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001073
David Rileye0684ad2017-04-05 00:02:59 -07001074
Dave Borowitz67700e92012-10-23 15:00:54 -07001075class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001076 _ALPHA = 0.5
1077
Dave Borowitz67700e92012-10-23 15:00:54 -07001078 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001079 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001080 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001081 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001082
1083 def Get(self, project):
1084 self._Load()
1085 return self._times.get(project.name, _ONE_DAY_S)
1086
1087 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001088 self._Load()
1089 name = project.name
1090 old = self._times.get(name, t)
1091 self._seen.add(name)
1092 a = self._ALPHA
1093 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001094
1095 def _Load(self):
1096 if self._times is None:
1097 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001098 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001099 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001100 except (IOError, ValueError):
1101 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001102 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001103 except OSError:
1104 pass
1105 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001106
1107 def Save(self):
1108 if self._times is None:
1109 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001110
1111 to_delete = []
1112 for name in self._times:
1113 if name not in self._seen:
1114 to_delete.append(name)
1115 for name in to_delete:
1116 del self._times[name]
1117
Dave Borowitz67700e92012-10-23 15:00:54 -07001118 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001119 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001120 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001121 except (IOError, TypeError):
1122 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001123 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001124 except OSError:
1125 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001126
1127# This is a replacement for xmlrpc.client.Transport using urllib2
1128# and supporting persistent-http[s]. It cannot change hosts from
1129# request to request like the normal transport, the real url
1130# is passed during initialization.
1131class PersistentTransport(xmlrpc.client.Transport):
1132 def __init__(self, orig_host):
1133 self.orig_host = orig_host
1134
1135 def request(self, host, handler, request_body, verbose=False):
1136 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1137 # Python doesn't understand cookies with the #HttpOnly_ prefix
1138 # Since we're only using them for HTTP, copy the file temporarily,
1139 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001140 if cookiefile:
1141 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001142 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001143 try:
1144 with open(cookiefile) as f:
1145 for line in f:
1146 if line.startswith("#HttpOnly_"):
1147 line = line[len("#HttpOnly_"):]
1148 tmpcookiefile.write(line)
1149 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001150
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001151 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001152 try:
1153 cookiejar.load()
1154 except cookielib.LoadError:
1155 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001156 finally:
1157 tmpcookiefile.close()
1158 else:
1159 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001160
1161 proxyhandler = urllib.request.ProxyHandler
1162 if proxy:
1163 proxyhandler = urllib.request.ProxyHandler({
1164 "http": proxy,
1165 "https": proxy })
1166
1167 opener = urllib.request.build_opener(
1168 urllib.request.HTTPCookieProcessor(cookiejar),
1169 proxyhandler)
1170
1171 url = urllib.parse.urljoin(self.orig_host, handler)
1172 parse_results = urllib.parse.urlparse(url)
1173
1174 scheme = parse_results.scheme
1175 if scheme == 'persistent-http':
1176 scheme = 'http'
1177 if scheme == 'persistent-https':
1178 # If we're proxying through persistent-https, use http. The
1179 # proxy itself will do the https.
1180 if proxy:
1181 scheme = 'http'
1182 else:
1183 scheme = 'https'
1184
1185 # Parse out any authentication information using the base class
1186 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1187
1188 url = urllib.parse.urlunparse((
1189 scheme,
1190 host,
1191 parse_results.path,
1192 parse_results.params,
1193 parse_results.query,
1194 parse_results.fragment))
1195
1196 request = urllib.request.Request(url, request_body)
1197 if extra_headers is not None:
1198 for (name, header) in extra_headers:
1199 request.add_header(name, header)
1200 request.add_header('Content-Type', 'text/xml')
1201 try:
1202 response = opener.open(request)
1203 except urllib.error.HTTPError as e:
1204 if e.code == 501:
1205 # We may have been redirected through a login process
1206 # but our POST turned into a GET. Retry.
1207 response = opener.open(request)
1208 else:
1209 raise
1210
1211 p, u = xmlrpc.client.getparser()
1212 while 1:
1213 data = response.read(1024)
1214 if not data:
1215 break
1216 p.feed(data)
1217 p.close()
1218 return u.close()
1219
1220 def close(self):
1221 pass
1222