blob: 9ed84b90a142fb97716f98984fd5652b8142d79d [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
Tim Kilbourn07669002013-03-08 15:02:49 -0800375 def _ReloadManifest(self, manifest_name=None):
376 if manifest_name:
377 # Override calls _Unload already
378 self.manifest.Override(manifest_name)
379 else:
380 self.manifest._Unload()
381
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700382 def UpdateProjectList(self):
383 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700384 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700385 if project.relpath:
386 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700387 file_name = 'project.list'
388 file_path = os.path.join(self.manifest.repodir, file_name)
389 old_project_paths = []
390
391 if os.path.exists(file_path):
392 fd = open(file_path, 'r')
393 try:
394 old_project_paths = fd.read().split('\n')
395 finally:
396 fd.close()
397 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700398 if not path:
399 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700400 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900401 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400402 if os.path.exists(self.manifest.topdir + '/' + path):
David Pursehousec1b86a22012-11-14 11:36:51 +0900403 project = Project(
404 manifest = self.manifest,
405 name = path,
406 remote = RemoteSpec('origin'),
407 gitdir = os.path.join(self.manifest.topdir,
408 path, '.git'),
409 worktree = os.path.join(self.manifest.topdir, path),
410 relpath = path,
411 revisionExpr = 'HEAD',
412 revisionId = None,
413 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400414
David Pursehousec1b86a22012-11-14 11:36:51 +0900415 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900416 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900417 'are present' % project.relpath, file=sys.stderr)
418 print(' commit changes, then run sync again',
419 file=sys.stderr)
420 return -1
421 else:
422 print('Deleting obsolete path %s' % project.worktree,
423 file=sys.stderr)
424 shutil.rmtree(project.worktree)
425 # Try deleting parent subdirs if they are empty
426 project_dir = os.path.dirname(project.worktree)
427 while project_dir != self.manifest.topdir:
428 try:
429 os.rmdir(project_dir)
430 except OSError:
431 break
432 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700433
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700434 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700435 fd = open(file_path, 'w')
436 try:
437 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700438 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700439 finally:
440 fd.close()
441 return 0
442
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700443 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800444 if opt.jobs:
445 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700446 if self.jobs > 1:
447 soft_limit, _ = _rlimit_nofile()
448 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
449
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700450 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700451 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700452 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700453 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700454 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700455 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500456 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700457 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500458 sys.exit(1)
459 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700460 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500461 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900462 if opt.manifest_server_username or opt.manifest_server_password:
463 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700464 print('error: -u and -p may only be combined with -s or -t',
465 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900466 sys.exit(1)
467 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700468 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900469 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500470
471 if opt.manifest_name:
472 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700473
Victor Boivie08c880d2011-04-19 10:32:52 +0200474 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700475 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900476 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700477 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700478 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900479
480 manifest_server = self.manifest.manifest_server
David Pursehousecf76b1b2012-09-14 10:31:42 +0900481
David Pursehouse86d973d2012-08-24 10:21:02 +0900482 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900483 username = None
484 password = None
485 if opt.manifest_server_username and opt.manifest_server_password:
486 username = opt.manifest_server_username
487 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900488 else:
489 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900490 info = netrc.netrc()
491 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700492 print('.netrc file does not exist or could not be opened',
493 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900494 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900495 try:
496 parse_result = urlparse.urlparse(manifest_server)
497 if parse_result.hostname:
498 username, _account, password = \
499 info.authenticators(parse_result.hostname)
500 except TypeError:
501 # TypeError is raised when the given hostname is not present
502 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700503 print('No credentials found for %s in .netrc'
504 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700505 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700506 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900507
508 if (username and password):
509 manifest_server = manifest_server.replace('://', '://%s:%s@' %
510 (username, password),
511 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900512
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700513 try:
David Pursehouse86d973d2012-08-24 10:21:02 +0900514 server = xmlrpclib.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200515 if opt.smart_sync:
516 p = self.manifest.manifestProject
517 b = p.GetBranch(p.CurrentBranch)
518 branch = b.merge
519 if branch.startswith(R_HEADS):
520 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700521
Victor Boivie08c880d2011-04-19 10:32:52 +0200522 env = os.environ.copy()
523 if (env.has_key('TARGET_PRODUCT') and
524 env.has_key('TARGET_BUILD_VARIANT')):
525 target = '%s-%s' % (env['TARGET_PRODUCT'],
526 env['TARGET_BUILD_VARIANT'])
527 [success, manifest_str] = server.GetApprovedManifest(branch, target)
528 else:
529 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700530 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200531 assert(opt.smart_tag)
532 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700533
534 if success:
535 manifest_name = "smart_sync_override.xml"
536 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
537 manifest_name)
538 try:
539 f = open(manifest_path, 'w')
540 try:
541 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700542 finally:
543 f.close()
544 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700545 print('error: cannot write manifest to %s' % manifest_path,
546 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700547 sys.exit(1)
Nico Sallembien719965a2010-04-20 15:28:19 -0700548 self.manifest.Override(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700549 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700550 print('error: %s' % manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700551 sys.exit(1)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700552 except (socket.error, IOError, xmlrpclib.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700553 print('error: cannot connect to manifest server %s:\n%s'
554 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900555 sys.exit(1)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700556 except xmlrpclib.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700557 print('error: cannot connect to manifest server %s:\n%d %s'
558 % (self.manifest.manifest_server, e.errcode, e.errmsg),
559 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700560 sys.exit(1)
561
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700562 rp = self.manifest.repoProject
563 rp.PreSync()
564
565 mp = self.manifest.manifestProject
566 mp.PreSync()
567
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800568 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700569 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800570
Nico Sallembien9bb18162009-12-07 15:38:01 -0800571 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700572 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700573 current_branch_only=opt.current_branch_only,
574 no_tags=opt.no_tags)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800575
576 if mp.HasChanges:
577 syncbuf = SyncBuffer(mp.config)
578 mp.Sync_LocalHalf(syncbuf)
579 if not syncbuf.Finish():
580 sys.exit(1)
Tim Kilbourn07669002013-03-08 15:02:49 -0800581 self._ReloadManifest(opt.manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700582 if opt.jobs is None:
583 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800584 all_projects = self.GetProjects(args,
585 missing_ok=True,
586 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700587
Dave Borowitz67700e92012-10-23 15:00:54 -0700588 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700589 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700590 to_fetch = []
591 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700592 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700593 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900594 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700595 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700596
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800597 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700598 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700599 if opt.network_only:
600 # bail out now; the rest touches the working tree
601 return
602
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800603 # Iteratively fetch missing and/or nested unregistered submodules
604 previously_missing_set = set()
605 while True:
Tim Kilbourn07669002013-03-08 15:02:49 -0800606 self._ReloadManifest(opt.manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800607 all_projects = self.GetProjects(args,
608 missing_ok=True,
609 submodules_ok=opt.fetch_submodules)
610 missing = []
611 for project in all_projects:
612 if project.gitdir not in fetched:
613 missing.append(project)
614 if not missing:
615 break
616 # Stop us from non-stopped fetching actually-missing repos: If set of
617 # missing repos has not been changed from last fetch, we break.
618 missing_set = set(p.name for p in missing)
619 if previously_missing_set == missing_set:
620 break
621 previously_missing_set = missing_set
622 fetched.update(self._Fetch(missing, opt))
623
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700624 if self.manifest.IsMirror:
625 # bail out now, we have no working tree
626 return
627
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700628 if self.UpdateProjectList():
629 sys.exit(1)
630
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700631 syncbuf = SyncBuffer(mp.config,
632 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900633 pm = Progress('Syncing work tree', len(all_projects))
634 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700635 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800636 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700637 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700638 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700639 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700640 if not syncbuf.Finish():
641 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700642
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700643 # If there's a notice that's supposed to print at the end of the sync, print
644 # it now...
645 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700646 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700647
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700648def _PostRepoUpgrade(manifest, quiet=False):
Conley Owensc9129d92012-10-01 16:12:28 -0700649 wrapper = WrapperModule()
650 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700651 wrapper.SetupGnuPG(quiet)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700652 for project in manifest.projects.values():
653 if project.Exists:
654 project.PostRepoUpgrade()
655
656def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
657 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700658 print('info: A new version of repo is available', file=sys.stderr)
659 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700660 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700661 syncbuf = SyncBuffer(rp.config)
662 rp.Sync_LocalHalf(syncbuf)
663 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700664 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700665 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700666 raise RepoChangedException(['--repo-upgraded'])
667 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700668 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700669 else:
670 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700671 print('repo version %s is current' % rp.work_git.describe(HEAD),
672 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700673
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700674def _VerifyTag(project):
675 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
676 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700677 print('warning: GnuPG was not available during last "repo init"\n'
678 'warning: Cannot automatically authenticate repo."""',
679 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700680 return True
681
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700682 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700683 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700684 except GitError:
685 cur = None
686
687 if not cur \
688 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700689 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700690 if rev.startswith(R_HEADS):
691 rev = rev[len(R_HEADS):]
692
Sarah Owenscecd1d82012-11-01 22:59:27 -0700693 print(file=sys.stderr)
694 print("warning: project '%s' branch '%s' is not signed"
695 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700696 return False
697
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800698 env = os.environ.copy()
699 env['GIT_DIR'] = project.gitdir.encode()
700 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700701
702 cmd = [GIT, 'tag', '-v', cur]
703 proc = subprocess.Popen(cmd,
704 stdout = subprocess.PIPE,
705 stderr = subprocess.PIPE,
706 env = env)
707 out = proc.stdout.read()
708 proc.stdout.close()
709
710 err = proc.stderr.read()
711 proc.stderr.close()
712
713 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700714 print(file=sys.stderr)
715 print(out, file=sys.stderr)
716 print(err, file=sys.stderr)
717 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700718 return False
719 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700720
721class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700722 _ALPHA = 0.5
723
Dave Borowitz67700e92012-10-23 15:00:54 -0700724 def __init__(self, manifest):
725 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
726 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700727 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700728
729 def Get(self, project):
730 self._Load()
731 return self._times.get(project.name, _ONE_DAY_S)
732
733 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700734 self._Load()
735 name = project.name
736 old = self._times.get(name, t)
737 self._seen.add(name)
738 a = self._ALPHA
739 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700740
741 def _Load(self):
742 if self._times is None:
743 try:
744 f = open(self._path)
745 except IOError:
746 self._times = {}
747 return self._times
748 try:
749 try:
750 self._times = pickle.load(f)
David Pursehouse1d947b32012-10-25 12:23:11 +0900751 except IOError:
Dave Borowitz67700e92012-10-23 15:00:54 -0700752 try:
753 os.remove(self._path)
754 except OSError:
755 pass
756 self._times = {}
757 finally:
758 f.close()
759 return self._times
760
761 def Save(self):
762 if self._times is None:
763 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700764
765 to_delete = []
766 for name in self._times:
767 if name not in self._seen:
768 to_delete.append(name)
769 for name in to_delete:
770 del self._times[name]
771
Dave Borowitz67700e92012-10-23 15:00:54 -0700772 try:
773 f = open(self._path, 'wb')
774 try:
775 pickle.dump(self._times, f)
776 except (IOError, OSError, pickle.PickleError):
777 try:
778 os.remove(self._path)
779 except OSError:
780 pass
781 finally:
782 f.close()