blob: b50df099c605519998c45c57fd71d4828c81f136 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
David Pursehouse86d973d2012-08-24 10:21:02 +090017import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070018from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Dave Borowitz67700e92012-10-23 15:00:54 -070020import pickle
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070022import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Shawn O. Pearcef6906872009-04-18 10:49:00 -070026import time
David Pursehouse59bbb582013-05-17 10:49:33 +090027
28from pyversion import is_python3
29if is_python3():
Chirayu Desai217ea7d2013-03-01 19:14:38 +053030 import urllib.parse
David Pursehouse59bbb582013-05-17 10:49:33 +090031 import xmlrpc.client
32else:
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import imp
34 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053036 urllib = imp.new_module('urllib')
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053037 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 xmlrpc = imp.new_module('xmlrpc')
39 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Roy Lee18afd7f2010-05-09 04:32:08 +080041try:
42 import threading as _threading
43except ImportError:
44 import dummy_threading as _threading
45
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070046try:
47 import resource
48 def _rlimit_nofile():
49 return resource.getrlimit(resource.RLIMIT_NOFILE)
50except ImportError:
51 def _rlimit_nofile():
52 return (256, 256)
53
Dave Borowitz18857212012-10-23 17:02:59 -070054try:
55 import multiprocessing
56except ImportError:
57 multiprocessing = None
58
Dave Borowitze2152672012-10-31 12:24:38 -070059from git_command import GIT, git_require
David Pursehoused94aaef2012-09-07 09:52:04 +090060from git_refs import R_HEADS, HEAD
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070061from project import Project
62from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080063from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000064from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070065from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070066from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080067from wrapper import Wrapper
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068
Dave Borowitz67700e92012-10-23 15:00:54 -070069_ONE_DAY_S = 24 * 60 * 60
70
Doug Andersonfc06ced2011-03-16 15:49:18 -070071class _FetchError(Exception):
72 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
73 pass
74
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080075class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080076 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070077 common = True
78 helpSummary = "Update working tree to the latest revision"
79 helpUsage = """
80%prog [<project>...]
81"""
82 helpDescription = """
83The '%prog' command synchronizes local project directories
84with the remote repositories specified in the manifest. If a local
85project does not yet exist, it will clone a new local directory from
86the remote repository and set up tracking branches as specified in
87the manifest. If the local project already exists, '%prog'
88will update the remote branches and rebase any new local changes
89on top of the new remote changes.
90
91'%prog' will synchronize all projects listed at the command
92line. Projects can be specified either by name, or by a relative
93or absolute path to the project's local directory. If no projects
94are specified, '%prog' will synchronize all projects listed in
95the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070096
97The -d/--detach option can be used to switch specified projects
98back to the manifest revision. This option is especially helpful
99if the project is currently on a topic branch, but the manifest
100revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700101
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700102The -s/--smart-sync option can be used to sync to a known good
103build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200104manifest. The -t/--smart-tag option is similar and allows you to
105specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700106
David Pursehousecf76b1b2012-09-14 10:31:42 +0900107The -u/--manifest-server-username and -p/--manifest-server-password
108options can be used to specify a username and password to authenticate
109with the manifest server when using the -s or -t option.
110
111If -u and -p are not specified when using the -s or -t option, '%prog'
112will attempt to read authentication credentials for the manifest server
113from the user's .netrc file.
114
115'%prog' will not use authentication credentials from -u/-p or .netrc
116if the manifest server specified in the manifest file already includes
117credentials.
118
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500119The -f/--force-broken option can be used to proceed with syncing
120other projects if a project sync fails.
121
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700122The --no-clone-bundle option disables any attempt to use
123$URL/clone.bundle to bootstrap a new Git repository from a
124resumeable bundle file on a content delivery network. This
125may be necessary if there are problems with the local Python
126HTTP client or proxy configuration, but the Git binary works.
127
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800128The --fetch-submodules option enables fetching Git submodules
129of a project from server.
130
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700131SSH Connections
132---------------
133
134If at least one project remote URL uses an SSH connection (ssh://,
135git+ssh://, or user@host:path syntax) repo will automatically
136enable the SSH ControlMaster option when connecting to that host.
137This feature permits other projects in the same '%prog' session to
138reuse the same SSH tunnel, saving connection setup overheads.
139
140To disable this behavior on UNIX platforms, set the GIT_SSH
141environment variable to 'ssh'. For example:
142
143 export GIT_SSH=ssh
144 %prog
145
146Compatibility
147~~~~~~~~~~~~~
148
149This feature is automatically disabled on Windows, due to the lack
150of UNIX domain socket support.
151
152This feature is not compatible with url.insteadof rewrites in the
153user's ~/.gitconfig. '%prog' is currently not able to perform the
154rewrite early enough to establish the ControlMaster tunnel.
155
156If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
157later is required to fix a server side protocol bug.
158
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159"""
160
Nico Sallembien6623b212010-05-11 12:57:01 -0700161 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000162 try:
163 self.jobs = self.manifest.default.sync_j
164 except ManifestParseError:
165 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700166
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500167 p.add_option('-f', '--force-broken',
168 dest='force_broken', action='store_true',
169 help="continue sync even if a project fails to sync")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900170 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700171 dest='local_only', action='store_true',
172 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900173 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700174 dest='network_only', action='store_true',
175 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900176 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700177 dest='detach_head', action='store_true',
178 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900179 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700180 dest='current_branch_only', action='store_true',
181 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900182 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700183 dest='quiet', action='store_true',
184 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900185 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800186 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700187 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500188 p.add_option('-m', '--manifest-name',
189 dest='manifest_name',
190 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700191 p.add_option('--no-clone-bundle',
192 dest='no_clone_bundle', action='store_true',
193 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800194 p.add_option('-u', '--manifest-server-username', action='store',
195 dest='manifest_server_username',
196 help='username to authenticate with the manifest server')
197 p.add_option('-p', '--manifest-server-password', action='store',
198 dest='manifest_server_password',
199 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800200 p.add_option('--fetch-submodules',
201 dest='fetch_submodules', action='store_true',
202 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700203 p.add_option('--no-tags',
204 dest='no_tags', action='store_true',
205 help="don't fetch tags")
Nico Sallembien6623b212010-05-11 12:57:01 -0700206 if show_smart:
207 p.add_option('-s', '--smart-sync',
208 dest='smart_sync', action='store_true',
209 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200210 p.add_option('-t', '--smart-tag',
211 dest='smart_tag', action='store',
212 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700213
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700214 g = p.add_option_group('repo Version options')
215 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216 dest='no_repo_verify', action='store_true',
217 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700218 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800219 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700220 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700221
David James89ece422014-01-09 18:51:58 -0800222 def _FetchProjectList(self, opt, projects, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900223 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800224
David James8d201162013-10-11 17:03:19 -0700225 Delegates most of the work to _FetchHelper.
226
227 Args:
228 opt: Program options returned from optparse. See _Options().
229 projects: Projects to fetch.
David James89ece422014-01-09 18:51:58 -0800230 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700231 _FetchHelper docstring for details.
232 """
233 for project in projects:
David James89ece422014-01-09 18:51:58 -0800234 success = self._FetchHelper(opt, project, *args, **kwargs)
David James8d201162013-10-11 17:03:19 -0700235 if not success and not opt.force_broken:
236 break
237
238 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
239 """Fetch git objects for a single project.
240
David Pursehousec1b86a22012-11-14 11:36:51 +0900241 Args:
242 opt: Program options returned from optparse. See _Options().
243 project: Project object for the project to fetch.
244 lock: Lock for accessing objects that are shared amongst multiple
245 _FetchHelper() threads.
246 fetched: set object that we will add project.gitdir to when we're done
247 (with our lock held).
248 pm: Instance of a Project object. We will call pm.update() (with our
249 lock held).
250 sem: We'll release() this semaphore when we exit so that another thread
251 can be started up.
252 err_event: We'll set this event in the case of an error (after printing
253 out info about the error).
David James8d201162013-10-11 17:03:19 -0700254
255 Returns:
256 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900257 """
258 # We'll set to true once we've locked the lock.
259 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700260
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530261 if not opt.quiet:
262 print('Fetching project %s' % project.name)
263
David Pursehousec1b86a22012-11-14 11:36:51 +0900264 # Encapsulate everything in a try/except/finally so that:
265 # - We always set err_event in the case of an exception.
266 # - We always make sure we call sem.release().
267 # - We always make sure we unlock the lock if we locked it.
268 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700269 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900270 start = time.time()
271 success = project.Sync_NetworkHalf(
272 quiet=opt.quiet,
273 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700274 clone_bundle=not opt.no_clone_bundle,
Julien Campergue335f5ef2013-10-16 11:02:35 +0200275 no_tags=opt.no_tags, archive=self.manifest.IsArchive)
David Pursehousec1b86a22012-11-14 11:36:51 +0900276 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700277
David Pursehousec1b86a22012-11-14 11:36:51 +0900278 # Lock around all the rest of the code, since printing, updating a set
279 # and Progress.update() are not thread safe.
280 lock.acquire()
281 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700282
David Pursehousec1b86a22012-11-14 11:36:51 +0900283 if not success:
284 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
285 if opt.force_broken:
286 print('warn: --force-broken, continuing to sync',
287 file=sys.stderr)
288 else:
289 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700290
David Pursehousec1b86a22012-11-14 11:36:51 +0900291 fetched.add(project.gitdir)
292 pm.update()
293 except _FetchError:
294 err_event.set()
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400295 except Exception as e:
296 print('error: Cannot fetch %s (%s: %s)' \
297 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900298 err_event.set()
299 raise
300 finally:
301 if did_lock:
302 lock.release()
303 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800304
David James8d201162013-10-11 17:03:19 -0700305 return success
306
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700307 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700308 fetched = set()
David James89ece422014-01-09 18:51:58 -0800309 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700310 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800311
David James89ece422014-01-09 18:51:58 -0800312 objdir_project_map = dict()
313 for project in projects:
314 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700315
David James89ece422014-01-09 18:51:58 -0800316 threads = set()
317 sem = _threading.Semaphore(self.jobs)
318 err_event = _threading.Event()
319 for project_list in objdir_project_map.values():
320 # Check for any errors before running any more tasks.
321 # ...we'll let existing threads finish, though.
322 if err_event.isSet() and not opt.force_broken:
323 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700324
David James89ece422014-01-09 18:51:58 -0800325 sem.acquire()
326 kwargs = dict(opt=opt,
327 projects=project_list,
328 lock=lock,
329 fetched=fetched,
330 pm=pm,
331 sem=sem,
332 err_event=err_event)
333 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700334 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800335 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200336 # Ensure that Ctrl-C will not freeze the repo process.
337 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800338 threads.add(t)
339 t.start()
David James89ece422014-01-09 18:51:58 -0800340 else:
341 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800342
David James89ece422014-01-09 18:51:58 -0800343 for t in threads:
344 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800345
David James89ece422014-01-09 18:51:58 -0800346 # If we saw an error, exit with code 1 so that other scripts can check.
347 if err_event.isSet():
348 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
349 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700350
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700351 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700352 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700353
Julien Campergue335f5ef2013-10-16 11:02:35 +0200354 if not self.manifest.IsArchive:
355 self._GCProjects(projects)
356
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700357 return fetched
358
Dave Borowitz18857212012-10-23 17:02:59 -0700359 def _GCProjects(self, projects):
David James8d201162013-10-11 17:03:19 -0700360 gitdirs = {}
361 for project in projects:
362 gitdirs[project.gitdir] = project.bare_git
363
Dave Borowitze2152672012-10-31 12:24:38 -0700364 has_dash_c = git_require((1, 7, 2))
365 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700366 cpu_count = multiprocessing.cpu_count()
367 else:
368 cpu_count = 1
369 jobs = min(self.jobs, cpu_count)
370
371 if jobs < 2:
David James8d201162013-10-11 17:03:19 -0700372 for bare_git in gitdirs.values():
373 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700374 return
375
376 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
377
378 threads = set()
379 sem = _threading.Semaphore(jobs)
380 err_event = _threading.Event()
381
David James8d201162013-10-11 17:03:19 -0700382 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700383 try:
384 try:
David James8d201162013-10-11 17:03:19 -0700385 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700386 except GitError:
387 err_event.set()
388 except:
389 err_event.set()
390 raise
391 finally:
392 sem.release()
393
David James8d201162013-10-11 17:03:19 -0700394 for bare_git in gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700395 if err_event.isSet():
396 break
397 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700398 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700399 t.daemon = True
400 threads.add(t)
401 t.start()
402
403 for t in threads:
404 t.join()
405
406 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700407 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700408 sys.exit(1)
409
Tim Kilbourn07669002013-03-08 15:02:49 -0800410 def _ReloadManifest(self, manifest_name=None):
411 if manifest_name:
412 # Override calls _Unload already
413 self.manifest.Override(manifest_name)
414 else:
415 self.manifest._Unload()
416
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700417 def UpdateProjectList(self):
418 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700419 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700420 if project.relpath:
421 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700422 file_name = 'project.list'
423 file_path = os.path.join(self.manifest.repodir, file_name)
424 old_project_paths = []
425
426 if os.path.exists(file_path):
427 fd = open(file_path, 'r')
428 try:
429 old_project_paths = fd.read().split('\n')
430 finally:
431 fd.close()
432 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700433 if not path:
434 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700435 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900436 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400437 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700438 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900439 project = Project(
440 manifest = self.manifest,
441 name = path,
442 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700443 gitdir = gitdir,
444 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900445 worktree = os.path.join(self.manifest.topdir, path),
446 relpath = path,
447 revisionExpr = 'HEAD',
448 revisionId = None,
449 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400450
David Pursehousec1b86a22012-11-14 11:36:51 +0900451 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900452 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900453 'are present' % project.relpath, file=sys.stderr)
454 print(' commit changes, then run sync again',
455 file=sys.stderr)
456 return -1
457 else:
458 print('Deleting obsolete path %s' % project.worktree,
459 file=sys.stderr)
460 shutil.rmtree(project.worktree)
461 # Try deleting parent subdirs if they are empty
462 project_dir = os.path.dirname(project.worktree)
463 while project_dir != self.manifest.topdir:
464 try:
465 os.rmdir(project_dir)
466 except OSError:
467 break
468 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700469
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700470 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700471 fd = open(file_path, 'w')
472 try:
473 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700474 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700475 finally:
476 fd.close()
477 return 0
478
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700479 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800480 if opt.jobs:
481 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700482 if self.jobs > 1:
483 soft_limit, _ = _rlimit_nofile()
484 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
485
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700486 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700487 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700488 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700489 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700490 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700491 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500492 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700493 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500494 sys.exit(1)
495 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700496 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500497 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900498 if opt.manifest_server_username or opt.manifest_server_password:
499 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700500 print('error: -u and -p may only be combined with -s or -t',
501 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900502 sys.exit(1)
503 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700504 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900505 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500506
507 if opt.manifest_name:
508 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700509
Chirayu Desaia892b102013-06-11 14:18:46 +0530510 manifest_name = opt.manifest_name
511
Victor Boivie08c880d2011-04-19 10:32:52 +0200512 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700513 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900514 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700515 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700516 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900517
518 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900519 if not opt.quiet:
520 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900521
David Pursehouse86d973d2012-08-24 10:21:02 +0900522 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900523 username = None
524 password = None
525 if opt.manifest_server_username and opt.manifest_server_password:
526 username = opt.manifest_server_username
527 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900528 else:
529 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900530 info = netrc.netrc()
531 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700532 print('.netrc file does not exist or could not be opened',
533 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900534 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900535 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530536 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900537 if parse_result.hostname:
538 username, _account, password = \
539 info.authenticators(parse_result.hostname)
540 except TypeError:
541 # TypeError is raised when the given hostname is not present
542 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700543 print('No credentials found for %s in .netrc'
544 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700545 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700546 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900547
548 if (username and password):
549 manifest_server = manifest_server.replace('://', '://%s:%s@' %
550 (username, password),
551 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900552
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700553 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530554 server = xmlrpc.client.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200555 if opt.smart_sync:
556 p = self.manifest.manifestProject
557 b = p.GetBranch(p.CurrentBranch)
558 branch = b.merge
559 if branch.startswith(R_HEADS):
560 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700561
Victor Boivie08c880d2011-04-19 10:32:52 +0200562 env = os.environ.copy()
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530563 if 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200564 target = '%s-%s' % (env['TARGET_PRODUCT'],
565 env['TARGET_BUILD_VARIANT'])
566 [success, manifest_str] = server.GetApprovedManifest(branch, target)
567 else:
568 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700569 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200570 assert(opt.smart_tag)
571 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700572
573 if success:
574 manifest_name = "smart_sync_override.xml"
575 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
576 manifest_name)
577 try:
578 f = open(manifest_path, 'w')
579 try:
580 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700581 finally:
582 f.close()
583 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700584 print('error: cannot write manifest to %s' % manifest_path,
585 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700586 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100587 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700588 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900589 print('error: manifest server RPC call failed: %s' %
590 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700591 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530592 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700593 print('error: cannot connect to manifest server %s:\n%s'
594 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900595 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530596 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700597 print('error: cannot connect to manifest server %s:\n%d %s'
598 % (self.manifest.manifest_server, e.errcode, e.errmsg),
599 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700600 sys.exit(1)
601
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700602 rp = self.manifest.repoProject
603 rp.PreSync()
604
605 mp = self.manifest.manifestProject
606 mp.PreSync()
607
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800608 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700609 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800610
Nico Sallembien9bb18162009-12-07 15:38:01 -0800611 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700612 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700613 current_branch_only=opt.current_branch_only,
614 no_tags=opt.no_tags)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800615
616 if mp.HasChanges:
617 syncbuf = SyncBuffer(mp.config)
618 mp.Sync_LocalHalf(syncbuf)
619 if not syncbuf.Finish():
620 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100621 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700622 if opt.jobs is None:
623 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800624 all_projects = self.GetProjects(args,
625 missing_ok=True,
626 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700627
Dave Borowitz67700e92012-10-23 15:00:54 -0700628 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700629 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700630 to_fetch = []
631 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700632 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700633 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900634 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700635 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700636
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800637 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700638 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700639 if opt.network_only:
640 # bail out now; the rest touches the working tree
641 return
642
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800643 # Iteratively fetch missing and/or nested unregistered submodules
644 previously_missing_set = set()
645 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100646 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800647 all_projects = self.GetProjects(args,
648 missing_ok=True,
649 submodules_ok=opt.fetch_submodules)
650 missing = []
651 for project in all_projects:
652 if project.gitdir not in fetched:
653 missing.append(project)
654 if not missing:
655 break
656 # Stop us from non-stopped fetching actually-missing repos: If set of
657 # missing repos has not been changed from last fetch, we break.
658 missing_set = set(p.name for p in missing)
659 if previously_missing_set == missing_set:
660 break
661 previously_missing_set = missing_set
662 fetched.update(self._Fetch(missing, opt))
663
Julien Campergue335f5ef2013-10-16 11:02:35 +0200664 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700665 # bail out now, we have no working tree
666 return
667
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700668 if self.UpdateProjectList():
669 sys.exit(1)
670
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700671 syncbuf = SyncBuffer(mp.config,
672 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900673 pm = Progress('Syncing work tree', len(all_projects))
674 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700675 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800676 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700677 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700678 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700679 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700680 if not syncbuf.Finish():
681 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700682
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700683 # If there's a notice that's supposed to print at the end of the sync, print
684 # it now...
685 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700686 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700687
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700688def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800689 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700690 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700691 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800692 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700693 if project.Exists:
694 project.PostRepoUpgrade()
695
696def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
697 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700698 print('info: A new version of repo is available', file=sys.stderr)
699 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700700 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700701 syncbuf = SyncBuffer(rp.config)
702 rp.Sync_LocalHalf(syncbuf)
703 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700704 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700705 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700706 raise RepoChangedException(['--repo-upgraded'])
707 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700708 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700709 else:
710 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700711 print('repo version %s is current' % rp.work_git.describe(HEAD),
712 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700713
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700714def _VerifyTag(project):
715 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
716 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700717 print('warning: GnuPG was not available during last "repo init"\n'
718 'warning: Cannot automatically authenticate repo."""',
719 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700720 return True
721
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700722 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700723 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700724 except GitError:
725 cur = None
726
727 if not cur \
728 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700729 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700730 if rev.startswith(R_HEADS):
731 rev = rev[len(R_HEADS):]
732
Sarah Owenscecd1d82012-11-01 22:59:27 -0700733 print(file=sys.stderr)
734 print("warning: project '%s' branch '%s' is not signed"
735 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700736 return False
737
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800738 env = os.environ.copy()
739 env['GIT_DIR'] = project.gitdir.encode()
740 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700741
742 cmd = [GIT, 'tag', '-v', cur]
743 proc = subprocess.Popen(cmd,
744 stdout = subprocess.PIPE,
745 stderr = subprocess.PIPE,
746 env = env)
747 out = proc.stdout.read()
748 proc.stdout.close()
749
750 err = proc.stderr.read()
751 proc.stderr.close()
752
753 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700754 print(file=sys.stderr)
755 print(out, file=sys.stderr)
756 print(err, file=sys.stderr)
757 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700758 return False
759 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700760
761class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700762 _ALPHA = 0.5
763
Dave Borowitz67700e92012-10-23 15:00:54 -0700764 def __init__(self, manifest):
765 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
766 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700767 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700768
769 def Get(self, project):
770 self._Load()
771 return self._times.get(project.name, _ONE_DAY_S)
772
773 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700774 self._Load()
775 name = project.name
776 old = self._times.get(name, t)
777 self._seen.add(name)
778 a = self._ALPHA
779 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700780
781 def _Load(self):
782 if self._times is None:
783 try:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +0530784 f = open(self._path, 'rb')
Dave Borowitz67700e92012-10-23 15:00:54 -0700785 except IOError:
786 self._times = {}
787 return self._times
788 try:
789 try:
790 self._times = pickle.load(f)
David Pursehouse1d947b32012-10-25 12:23:11 +0900791 except IOError:
Dave Borowitz67700e92012-10-23 15:00:54 -0700792 try:
793 os.remove(self._path)
794 except OSError:
795 pass
796 self._times = {}
797 finally:
798 f.close()
799 return self._times
800
801 def Save(self):
802 if self._times is None:
803 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700804
805 to_delete = []
806 for name in self._times:
807 if name not in self._seen:
808 to_delete.append(name)
809 for name in to_delete:
810 del self._times[name]
811
Dave Borowitz67700e92012-10-23 15:00:54 -0700812 try:
813 f = open(self._path, 'wb')
814 try:
815 pickle.dump(self._times, f)
816 except (IOError, OSError, pickle.PickleError):
817 try:
818 os.remove(self._path)
819 except OSError:
820 pass
821 finally:
822 f.close()