blob: 6c903ff44819785ccda18bd2d47330d4b1c2bbbd [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 Pursehouse86d973d2012-08-24 10:21:02 +090027import urlparse
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070028import xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070029
Roy Lee18afd7f2010-05-09 04:32:08 +080030try:
31 import threading as _threading
32except ImportError:
33 import dummy_threading as _threading
34
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070035try:
36 import resource
37 def _rlimit_nofile():
38 return resource.getrlimit(resource.RLIMIT_NOFILE)
39except ImportError:
40 def _rlimit_nofile():
41 return (256, 256)
42
Dave Borowitz18857212012-10-23 17:02:59 -070043try:
44 import multiprocessing
45except ImportError:
46 multiprocessing = None
47
Dave Borowitze2152672012-10-31 12:24:38 -070048from git_command import GIT, git_require
David Pursehoused94aaef2012-09-07 09:52:04 +090049from git_refs import R_HEADS, HEAD
Conley Owensc9129d92012-10-01 16:12:28 -070050from main import WrapperModule
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070051from project import Project
52from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080053from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000054from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070055from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070056from progress import Progress
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070057
Dave Borowitz67700e92012-10-23 15:00:54 -070058_ONE_DAY_S = 24 * 60 * 60
59
Doug Andersonfc06ced2011-03-16 15:49:18 -070060class _FetchError(Exception):
61 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
62 pass
63
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080064class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080065 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070066 common = True
67 helpSummary = "Update working tree to the latest revision"
68 helpUsage = """
69%prog [<project>...]
70"""
71 helpDescription = """
72The '%prog' command synchronizes local project directories
73with the remote repositories specified in the manifest. If a local
74project does not yet exist, it will clone a new local directory from
75the remote repository and set up tracking branches as specified in
76the manifest. If the local project already exists, '%prog'
77will update the remote branches and rebase any new local changes
78on top of the new remote changes.
79
80'%prog' will synchronize all projects listed at the command
81line. Projects can be specified either by name, or by a relative
82or absolute path to the project's local directory. If no projects
83are specified, '%prog' will synchronize all projects listed in
84the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070085
86The -d/--detach option can be used to switch specified projects
87back to the manifest revision. This option is especially helpful
88if the project is currently on a topic branch, but the manifest
89revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070090
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070091The -s/--smart-sync option can be used to sync to a known good
92build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020093manifest. The -t/--smart-tag option is similar and allows you to
94specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070095
David Pursehousecf76b1b2012-09-14 10:31:42 +090096The -u/--manifest-server-username and -p/--manifest-server-password
97options can be used to specify a username and password to authenticate
98with the manifest server when using the -s or -t option.
99
100If -u and -p are not specified when using the -s or -t option, '%prog'
101will attempt to read authentication credentials for the manifest server
102from the user's .netrc file.
103
104'%prog' will not use authentication credentials from -u/-p or .netrc
105if the manifest server specified in the manifest file already includes
106credentials.
107
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500108The -f/--force-broken option can be used to proceed with syncing
109other projects if a project sync fails.
110
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700111The --no-clone-bundle option disables any attempt to use
112$URL/clone.bundle to bootstrap a new Git repository from a
113resumeable bundle file on a content delivery network. This
114may be necessary if there are problems with the local Python
115HTTP client or proxy configuration, but the Git binary works.
116
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800117The --fetch-submodules option enables fetching Git submodules
118of a project from server.
119
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700120SSH Connections
121---------------
122
123If at least one project remote URL uses an SSH connection (ssh://,
124git+ssh://, or user@host:path syntax) repo will automatically
125enable the SSH ControlMaster option when connecting to that host.
126This feature permits other projects in the same '%prog' session to
127reuse the same SSH tunnel, saving connection setup overheads.
128
129To disable this behavior on UNIX platforms, set the GIT_SSH
130environment variable to 'ssh'. For example:
131
132 export GIT_SSH=ssh
133 %prog
134
135Compatibility
136~~~~~~~~~~~~~
137
138This feature is automatically disabled on Windows, due to the lack
139of UNIX domain socket support.
140
141This feature is not compatible with url.insteadof rewrites in the
142user's ~/.gitconfig. '%prog' is currently not able to perform the
143rewrite early enough to establish the ControlMaster tunnel.
144
145If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
146later is required to fix a server side protocol bug.
147
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700148"""
149
Nico Sallembien6623b212010-05-11 12:57:01 -0700150 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000151 try:
152 self.jobs = self.manifest.default.sync_j
153 except ManifestParseError:
154 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700155
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500156 p.add_option('-f', '--force-broken',
157 dest='force_broken', action='store_true',
158 help="continue sync even if a project fails to sync")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900159 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700160 dest='local_only', action='store_true',
161 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900162 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700163 dest='network_only', action='store_true',
164 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900165 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700166 dest='detach_head', action='store_true',
167 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900168 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700169 dest='current_branch_only', action='store_true',
170 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900171 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700172 dest='quiet', action='store_true',
173 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900174 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800175 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700176 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500177 p.add_option('-m', '--manifest-name',
178 dest='manifest_name',
179 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700180 p.add_option('--no-clone-bundle',
181 dest='no_clone_bundle', action='store_true',
182 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800183 p.add_option('-u', '--manifest-server-username', action='store',
184 dest='manifest_server_username',
185 help='username to authenticate with the manifest server')
186 p.add_option('-p', '--manifest-server-password', action='store',
187 dest='manifest_server_password',
188 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800189 p.add_option('--fetch-submodules',
190 dest='fetch_submodules', action='store_true',
191 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700192 p.add_option('--no-tags',
193 dest='no_tags', action='store_true',
194 help="don't fetch tags")
Nico Sallembien6623b212010-05-11 12:57:01 -0700195 if show_smart:
196 p.add_option('-s', '--smart-sync',
197 dest='smart_sync', action='store_true',
198 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200199 p.add_option('-t', '--smart-tag',
200 dest='smart_tag', action='store',
201 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700202
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700203 g = p.add_option_group('repo Version options')
204 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700205 dest='no_repo_verify', action='store_true',
206 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700207 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800208 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700209 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700210
Doug Andersonfc06ced2011-03-16 15:49:18 -0700211 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
David Pursehousec1b86a22012-11-14 11:36:51 +0900212 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800213
David Pursehousec1b86a22012-11-14 11:36:51 +0900214 Args:
215 opt: Program options returned from optparse. See _Options().
216 project: Project object for the project to fetch.
217 lock: Lock for accessing objects that are shared amongst multiple
218 _FetchHelper() threads.
219 fetched: set object that we will add project.gitdir to when we're done
220 (with our lock held).
221 pm: Instance of a Project object. We will call pm.update() (with our
222 lock held).
223 sem: We'll release() this semaphore when we exit so that another thread
224 can be started up.
225 err_event: We'll set this event in the case of an error (after printing
226 out info about the error).
227 """
228 # We'll set to true once we've locked the lock.
229 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700230
David Pursehousec1b86a22012-11-14 11:36:51 +0900231 # Encapsulate everything in a try/except/finally so that:
232 # - We always set err_event in the case of an exception.
233 # - We always make sure we call sem.release().
234 # - We always make sure we unlock the lock if we locked it.
235 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700236 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900237 start = time.time()
238 success = project.Sync_NetworkHalf(
239 quiet=opt.quiet,
240 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700241 clone_bundle=not opt.no_clone_bundle,
242 no_tags=opt.no_tags)
David Pursehousec1b86a22012-11-14 11:36:51 +0900243 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700244
David Pursehousec1b86a22012-11-14 11:36:51 +0900245 # Lock around all the rest of the code, since printing, updating a set
246 # and Progress.update() are not thread safe.
247 lock.acquire()
248 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700249
David Pursehousec1b86a22012-11-14 11:36:51 +0900250 if not success:
251 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
252 if opt.force_broken:
253 print('warn: --force-broken, continuing to sync',
254 file=sys.stderr)
255 else:
256 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700257
David Pursehousec1b86a22012-11-14 11:36:51 +0900258 fetched.add(project.gitdir)
259 pm.update()
260 except _FetchError:
261 err_event.set()
262 except:
263 err_event.set()
264 raise
265 finally:
266 if did_lock:
267 lock.release()
268 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800269
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700270 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700272 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800273
274 if self.jobs == 1:
275 for project in projects:
276 pm.update()
Shawn O. Pearce5d0efdb2012-08-02 12:13:01 -0700277 if project.Sync_NetworkHalf(
278 quiet=opt.quiet,
279 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700280 clone_bundle=not opt.no_clone_bundle,
281 no_tags=opt.no_tags):
Roy Lee18afd7f2010-05-09 04:32:08 +0800282 fetched.add(project.gitdir)
283 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700284 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500285 if opt.force_broken:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700286 print('warn: --force-broken, continuing to sync', file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500287 else:
288 sys.exit(1)
Roy Lee18afd7f2010-05-09 04:32:08 +0800289 else:
290 threads = set()
291 lock = _threading.Lock()
292 sem = _threading.Semaphore(self.jobs)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700293 err_event = _threading.Event()
Roy Lee18afd7f2010-05-09 04:32:08 +0800294 for project in projects:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700295 # Check for any errors before starting any new threads.
296 # ...we'll let existing threads finish, though.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400297 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700298 break
299
Roy Lee18afd7f2010-05-09 04:32:08 +0800300 sem.acquire()
301 t = _threading.Thread(target = self._FetchHelper,
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700302 args = (opt,
303 project,
304 lock,
305 fetched,
306 pm,
Doug Andersonfc06ced2011-03-16 15:49:18 -0700307 sem,
308 err_event))
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200309 # Ensure that Ctrl-C will not freeze the repo process.
310 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800311 threads.add(t)
312 t.start()
313
314 for t in threads:
315 t.join()
316
Doug Andersonfc06ced2011-03-16 15:49:18 -0700317 # If we saw an error, exit with code 1 so that other scripts can check.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400318 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700319 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700320 sys.exit(1)
321
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700322 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700323 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700324
325 self._GCProjects(projects)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700326 return fetched
327
Dave Borowitz18857212012-10-23 17:02:59 -0700328 def _GCProjects(self, projects):
Dave Borowitze2152672012-10-31 12:24:38 -0700329 has_dash_c = git_require((1, 7, 2))
330 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700331 cpu_count = multiprocessing.cpu_count()
332 else:
333 cpu_count = 1
334 jobs = min(self.jobs, cpu_count)
335
336 if jobs < 2:
337 for project in projects:
338 project.bare_git.gc('--auto')
339 return
340
341 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
342
343 threads = set()
344 sem = _threading.Semaphore(jobs)
345 err_event = _threading.Event()
346
347 def GC(project):
348 try:
349 try:
350 project.bare_git.gc('--auto', config=config)
351 except GitError:
352 err_event.set()
353 except:
354 err_event.set()
355 raise
356 finally:
357 sem.release()
358
359 for project in projects:
360 if err_event.isSet():
361 break
362 sem.acquire()
363 t = _threading.Thread(target=GC, args=(project,))
364 t.daemon = True
365 threads.add(t)
366 t.start()
367
368 for t in threads:
369 t.join()
370
371 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700372 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700373 sys.exit(1)
374
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700375 def UpdateProjectList(self):
376 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700377 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700378 if project.relpath:
379 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700380 file_name = 'project.list'
381 file_path = os.path.join(self.manifest.repodir, file_name)
382 old_project_paths = []
383
384 if os.path.exists(file_path):
385 fd = open(file_path, 'r')
386 try:
387 old_project_paths = fd.read().split('\n')
388 finally:
389 fd.close()
390 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700391 if not path:
392 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700393 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900394 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400395 if os.path.exists(self.manifest.topdir + '/' + path):
David Pursehousec1b86a22012-11-14 11:36:51 +0900396 project = Project(
397 manifest = self.manifest,
398 name = path,
399 remote = RemoteSpec('origin'),
400 gitdir = os.path.join(self.manifest.topdir,
401 path, '.git'),
402 worktree = os.path.join(self.manifest.topdir, path),
403 relpath = path,
404 revisionExpr = 'HEAD',
405 revisionId = None,
406 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400407
David Pursehousec1b86a22012-11-14 11:36:51 +0900408 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900409 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900410 'are present' % project.relpath, file=sys.stderr)
411 print(' commit changes, then run sync again',
412 file=sys.stderr)
413 return -1
414 else:
415 print('Deleting obsolete path %s' % project.worktree,
416 file=sys.stderr)
417 shutil.rmtree(project.worktree)
418 # Try deleting parent subdirs if they are empty
419 project_dir = os.path.dirname(project.worktree)
420 while project_dir != self.manifest.topdir:
421 try:
422 os.rmdir(project_dir)
423 except OSError:
424 break
425 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700426
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700427 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700428 fd = open(file_path, 'w')
429 try:
430 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700431 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700432 finally:
433 fd.close()
434 return 0
435
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700436 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800437 if opt.jobs:
438 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700439 if self.jobs > 1:
440 soft_limit, _ = _rlimit_nofile()
441 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
442
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700443 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700444 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700445 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700446 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700447 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700448 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500449 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700450 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500451 sys.exit(1)
452 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700453 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500454 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900455 if opt.manifest_server_username or opt.manifest_server_password:
456 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700457 print('error: -u and -p may only be combined with -s or -t',
458 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900459 sys.exit(1)
460 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700461 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900462 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500463
464 if opt.manifest_name:
465 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700466
Victor Boivie08c880d2011-04-19 10:32:52 +0200467 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700468 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900469 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700470 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700471 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900472
473 manifest_server = self.manifest.manifest_server
David Pursehousecf76b1b2012-09-14 10:31:42 +0900474
David Pursehouse86d973d2012-08-24 10:21:02 +0900475 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900476 username = None
477 password = None
478 if opt.manifest_server_username and opt.manifest_server_password:
479 username = opt.manifest_server_username
480 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900481 else:
482 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900483 info = netrc.netrc()
484 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700485 print('.netrc file does not exist or could not be opened',
486 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900487 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900488 try:
489 parse_result = urlparse.urlparse(manifest_server)
490 if parse_result.hostname:
491 username, _account, password = \
492 info.authenticators(parse_result.hostname)
493 except TypeError:
494 # TypeError is raised when the given hostname is not present
495 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700496 print('No credentials found for %s in .netrc'
497 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700498 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700499 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900500
501 if (username and password):
502 manifest_server = manifest_server.replace('://', '://%s:%s@' %
503 (username, password),
504 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900505
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700506 try:
David Pursehouse86d973d2012-08-24 10:21:02 +0900507 server = xmlrpclib.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200508 if opt.smart_sync:
509 p = self.manifest.manifestProject
510 b = p.GetBranch(p.CurrentBranch)
511 branch = b.merge
512 if branch.startswith(R_HEADS):
513 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700514
Victor Boivie08c880d2011-04-19 10:32:52 +0200515 env = os.environ.copy()
516 if (env.has_key('TARGET_PRODUCT') and
517 env.has_key('TARGET_BUILD_VARIANT')):
518 target = '%s-%s' % (env['TARGET_PRODUCT'],
519 env['TARGET_BUILD_VARIANT'])
520 [success, manifest_str] = server.GetApprovedManifest(branch, target)
521 else:
522 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700523 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200524 assert(opt.smart_tag)
525 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700526
527 if success:
528 manifest_name = "smart_sync_override.xml"
529 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
530 manifest_name)
531 try:
532 f = open(manifest_path, 'w')
533 try:
534 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700535 finally:
536 f.close()
537 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700538 print('error: cannot write manifest to %s' % manifest_path,
539 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700540 sys.exit(1)
Nico Sallembien719965a2010-04-20 15:28:19 -0700541 self.manifest.Override(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700542 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700543 print('error: %s' % manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700544 sys.exit(1)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700545 except (socket.error, IOError, xmlrpclib.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700546 print('error: cannot connect to manifest server %s:\n%s'
547 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900548 sys.exit(1)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700549 except xmlrpclib.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700550 print('error: cannot connect to manifest server %s:\n%d %s'
551 % (self.manifest.manifest_server, e.errcode, e.errmsg),
552 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700553 sys.exit(1)
554
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700555 rp = self.manifest.repoProject
556 rp.PreSync()
557
558 mp = self.manifest.manifestProject
559 mp.PreSync()
560
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800561 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700562 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800563
Nico Sallembien9bb18162009-12-07 15:38:01 -0800564 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700565 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700566 current_branch_only=opt.current_branch_only,
567 no_tags=opt.no_tags)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800568
569 if mp.HasChanges:
570 syncbuf = SyncBuffer(mp.config)
571 mp.Sync_LocalHalf(syncbuf)
572 if not syncbuf.Finish():
573 sys.exit(1)
574 self.manifest._Unload()
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700575 if opt.jobs is None:
576 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800577 all_projects = self.GetProjects(args,
578 missing_ok=True,
579 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700580
Dave Borowitz67700e92012-10-23 15:00:54 -0700581 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700582 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700583 to_fetch = []
584 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700585 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700586 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900587 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700588 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700589
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800590 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700591 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700592 if opt.network_only:
593 # bail out now; the rest touches the working tree
594 return
595
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800596 # Iteratively fetch missing and/or nested unregistered submodules
597 previously_missing_set = set()
598 while True:
599 self.manifest._Unload()
600 all_projects = self.GetProjects(args,
601 missing_ok=True,
602 submodules_ok=opt.fetch_submodules)
603 missing = []
604 for project in all_projects:
605 if project.gitdir not in fetched:
606 missing.append(project)
607 if not missing:
608 break
609 # Stop us from non-stopped fetching actually-missing repos: If set of
610 # missing repos has not been changed from last fetch, we break.
611 missing_set = set(p.name for p in missing)
612 if previously_missing_set == missing_set:
613 break
614 previously_missing_set = missing_set
615 fetched.update(self._Fetch(missing, opt))
616
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700617 if self.manifest.IsMirror:
618 # bail out now, we have no working tree
619 return
620
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700621 if self.UpdateProjectList():
622 sys.exit(1)
623
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700624 syncbuf = SyncBuffer(mp.config,
625 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900626 pm = Progress('Syncing work tree', len(all_projects))
627 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700628 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800629 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700630 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700631 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700632 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700633 if not syncbuf.Finish():
634 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700635
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700636 # If there's a notice that's supposed to print at the end of the sync, print
637 # it now...
638 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700639 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700640
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700641def _PostRepoUpgrade(manifest, quiet=False):
Conley Owensc9129d92012-10-01 16:12:28 -0700642 wrapper = WrapperModule()
643 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700644 wrapper.SetupGnuPG(quiet)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700645 for project in manifest.projects.values():
646 if project.Exists:
647 project.PostRepoUpgrade()
648
649def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
650 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700651 print('info: A new version of repo is available', file=sys.stderr)
652 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700653 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700654 syncbuf = SyncBuffer(rp.config)
655 rp.Sync_LocalHalf(syncbuf)
656 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700657 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700658 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700659 raise RepoChangedException(['--repo-upgraded'])
660 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700661 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700662 else:
663 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700664 print('repo version %s is current' % rp.work_git.describe(HEAD),
665 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700666
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700667def _VerifyTag(project):
668 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
669 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700670 print('warning: GnuPG was not available during last "repo init"\n'
671 'warning: Cannot automatically authenticate repo."""',
672 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700673 return True
674
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700675 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700676 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700677 except GitError:
678 cur = None
679
680 if not cur \
681 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700682 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700683 if rev.startswith(R_HEADS):
684 rev = rev[len(R_HEADS):]
685
Sarah Owenscecd1d82012-11-01 22:59:27 -0700686 print(file=sys.stderr)
687 print("warning: project '%s' branch '%s' is not signed"
688 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700689 return False
690
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800691 env = os.environ.copy()
692 env['GIT_DIR'] = project.gitdir.encode()
693 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700694
695 cmd = [GIT, 'tag', '-v', cur]
696 proc = subprocess.Popen(cmd,
697 stdout = subprocess.PIPE,
698 stderr = subprocess.PIPE,
699 env = env)
700 out = proc.stdout.read()
701 proc.stdout.close()
702
703 err = proc.stderr.read()
704 proc.stderr.close()
705
706 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700707 print(file=sys.stderr)
708 print(out, file=sys.stderr)
709 print(err, file=sys.stderr)
710 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700711 return False
712 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700713
714class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700715 _ALPHA = 0.5
716
Dave Borowitz67700e92012-10-23 15:00:54 -0700717 def __init__(self, manifest):
718 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
719 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700720 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700721
722 def Get(self, project):
723 self._Load()
724 return self._times.get(project.name, _ONE_DAY_S)
725
726 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700727 self._Load()
728 name = project.name
729 old = self._times.get(name, t)
730 self._seen.add(name)
731 a = self._ALPHA
732 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700733
734 def _Load(self):
735 if self._times is None:
736 try:
737 f = open(self._path)
738 except IOError:
739 self._times = {}
740 return self._times
741 try:
742 try:
743 self._times = pickle.load(f)
David Pursehouse1d947b32012-10-25 12:23:11 +0900744 except IOError:
Dave Borowitz67700e92012-10-23 15:00:54 -0700745 try:
746 os.remove(self._path)
747 except OSError:
748 pass
749 self._times = {}
750 finally:
751 f.close()
752 return self._times
753
754 def Save(self):
755 if self._times is None:
756 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700757
758 to_delete = []
759 for name in self._times:
760 if name not in self._seen:
761 to_delete.append(name)
762 for name in to_delete:
763 del self._times[name]
764
Dave Borowitz67700e92012-10-23 15:00:54 -0700765 try:
766 f = open(self._path, 'wb')
767 try:
768 pickle.dump(self._times, f)
769 except (IOError, OSError, pickle.PickleError):
770 try:
771 os.remove(self._path)
772 except OSError:
773 pass
774 finally:
775 f.close()