blob: 228a279a7f38273ad31bc0538a4556203a2c542f [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')
Nico Sallembien6623b212010-05-11 12:57:01 -0700192 if show_smart:
193 p.add_option('-s', '--smart-sync',
194 dest='smart_sync', action='store_true',
195 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200196 p.add_option('-t', '--smart-tag',
197 dest='smart_tag', action='store',
198 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700199
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700200 g = p.add_option_group('repo Version options')
201 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700202 dest='no_repo_verify', action='store_true',
203 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700204 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800205 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700206 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700207
Doug Andersonfc06ced2011-03-16 15:49:18 -0700208 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
David Pursehousec1b86a22012-11-14 11:36:51 +0900209 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800210
David Pursehousec1b86a22012-11-14 11:36:51 +0900211 Args:
212 opt: Program options returned from optparse. See _Options().
213 project: Project object for the project to fetch.
214 lock: Lock for accessing objects that are shared amongst multiple
215 _FetchHelper() threads.
216 fetched: set object that we will add project.gitdir to when we're done
217 (with our lock held).
218 pm: Instance of a Project object. We will call pm.update() (with our
219 lock held).
220 sem: We'll release() this semaphore when we exit so that another thread
221 can be started up.
222 err_event: We'll set this event in the case of an error (after printing
223 out info about the error).
224 """
225 # We'll set to true once we've locked the lock.
226 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700227
David Pursehousec1b86a22012-11-14 11:36:51 +0900228 # Encapsulate everything in a try/except/finally so that:
229 # - We always set err_event in the case of an exception.
230 # - We always make sure we call sem.release().
231 # - We always make sure we unlock the lock if we locked it.
232 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700233 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900234 start = time.time()
235 success = project.Sync_NetworkHalf(
236 quiet=opt.quiet,
237 current_branch_only=opt.current_branch_only,
238 clone_bundle=not opt.no_clone_bundle)
239 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700240
David Pursehousec1b86a22012-11-14 11:36:51 +0900241 # Lock around all the rest of the code, since printing, updating a set
242 # and Progress.update() are not thread safe.
243 lock.acquire()
244 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700245
David Pursehousec1b86a22012-11-14 11:36:51 +0900246 if not success:
247 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
248 if opt.force_broken:
249 print('warn: --force-broken, continuing to sync',
250 file=sys.stderr)
251 else:
252 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700253
David Pursehousec1b86a22012-11-14 11:36:51 +0900254 fetched.add(project.gitdir)
255 pm.update()
256 except _FetchError:
257 err_event.set()
258 except:
259 err_event.set()
260 raise
261 finally:
262 if did_lock:
263 lock.release()
264 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800265
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700266 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700268 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800269
270 if self.jobs == 1:
271 for project in projects:
272 pm.update()
Shawn O. Pearce5d0efdb2012-08-02 12:13:01 -0700273 if project.Sync_NetworkHalf(
274 quiet=opt.quiet,
275 current_branch_only=opt.current_branch_only,
276 clone_bundle=not opt.no_clone_bundle):
Roy Lee18afd7f2010-05-09 04:32:08 +0800277 fetched.add(project.gitdir)
278 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700279 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500280 if opt.force_broken:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700281 print('warn: --force-broken, continuing to sync', file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500282 else:
283 sys.exit(1)
Roy Lee18afd7f2010-05-09 04:32:08 +0800284 else:
285 threads = set()
286 lock = _threading.Lock()
287 sem = _threading.Semaphore(self.jobs)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700288 err_event = _threading.Event()
Roy Lee18afd7f2010-05-09 04:32:08 +0800289 for project in projects:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700290 # Check for any errors before starting any new threads.
291 # ...we'll let existing threads finish, though.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400292 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700293 break
294
Roy Lee18afd7f2010-05-09 04:32:08 +0800295 sem.acquire()
296 t = _threading.Thread(target = self._FetchHelper,
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700297 args = (opt,
298 project,
299 lock,
300 fetched,
301 pm,
Doug Andersonfc06ced2011-03-16 15:49:18 -0700302 sem,
303 err_event))
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200304 # Ensure that Ctrl-C will not freeze the repo process.
305 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800306 threads.add(t)
307 t.start()
308
309 for t in threads:
310 t.join()
311
Doug Andersonfc06ced2011-03-16 15:49:18 -0700312 # If we saw an error, exit with code 1 so that other scripts can check.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400313 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700314 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700315 sys.exit(1)
316
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700317 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700318 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700319
320 self._GCProjects(projects)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700321 return fetched
322
Dave Borowitz18857212012-10-23 17:02:59 -0700323 def _GCProjects(self, projects):
Dave Borowitze2152672012-10-31 12:24:38 -0700324 has_dash_c = git_require((1, 7, 2))
325 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700326 cpu_count = multiprocessing.cpu_count()
327 else:
328 cpu_count = 1
329 jobs = min(self.jobs, cpu_count)
330
331 if jobs < 2:
332 for project in projects:
333 project.bare_git.gc('--auto')
334 return
335
336 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
337
338 threads = set()
339 sem = _threading.Semaphore(jobs)
340 err_event = _threading.Event()
341
342 def GC(project):
343 try:
344 try:
345 project.bare_git.gc('--auto', config=config)
346 except GitError:
347 err_event.set()
348 except:
349 err_event.set()
350 raise
351 finally:
352 sem.release()
353
354 for project in projects:
355 if err_event.isSet():
356 break
357 sem.acquire()
358 t = _threading.Thread(target=GC, args=(project,))
359 t.daemon = True
360 threads.add(t)
361 t.start()
362
363 for t in threads:
364 t.join()
365
366 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700367 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700368 sys.exit(1)
369
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700370 def UpdateProjectList(self):
371 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700372 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700373 if project.relpath:
374 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700375 file_name = 'project.list'
376 file_path = os.path.join(self.manifest.repodir, file_name)
377 old_project_paths = []
378
379 if os.path.exists(file_path):
380 fd = open(file_path, 'r')
381 try:
382 old_project_paths = fd.read().split('\n')
383 finally:
384 fd.close()
385 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700386 if not path:
387 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700388 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900389 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400390 if os.path.exists(self.manifest.topdir + '/' + path):
David Pursehousec1b86a22012-11-14 11:36:51 +0900391 project = Project(
392 manifest = self.manifest,
393 name = path,
394 remote = RemoteSpec('origin'),
395 gitdir = os.path.join(self.manifest.topdir,
396 path, '.git'),
397 worktree = os.path.join(self.manifest.topdir, path),
398 relpath = path,
399 revisionExpr = 'HEAD',
400 revisionId = None,
401 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400402
David Pursehousec1b86a22012-11-14 11:36:51 +0900403 if project.IsDirty():
404 print('error: Cannot remove project "%s": uncommitted changes'
405 'are present' % project.relpath, file=sys.stderr)
406 print(' commit changes, then run sync again',
407 file=sys.stderr)
408 return -1
409 else:
410 print('Deleting obsolete path %s' % project.worktree,
411 file=sys.stderr)
412 shutil.rmtree(project.worktree)
413 # Try deleting parent subdirs if they are empty
414 project_dir = os.path.dirname(project.worktree)
415 while project_dir != self.manifest.topdir:
416 try:
417 os.rmdir(project_dir)
418 except OSError:
419 break
420 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700421
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700422 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700423 fd = open(file_path, 'w')
424 try:
425 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700426 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700427 finally:
428 fd.close()
429 return 0
430
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700431 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800432 if opt.jobs:
433 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700434 if self.jobs > 1:
435 soft_limit, _ = _rlimit_nofile()
436 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
437
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700438 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700439 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700440 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700441 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700442 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700443 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500444 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700445 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500446 sys.exit(1)
447 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700448 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500449 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900450 if opt.manifest_server_username or opt.manifest_server_password:
451 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700452 print('error: -u and -p may only be combined with -s or -t',
453 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900454 sys.exit(1)
455 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700456 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900457 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500458
459 if opt.manifest_name:
460 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700461
Victor Boivie08c880d2011-04-19 10:32:52 +0200462 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700463 if not self.manifest.manifest_server:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700464 print('error: cannot smart sync: no manifest server defined in'
465 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700466 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900467
468 manifest_server = self.manifest.manifest_server
David Pursehousecf76b1b2012-09-14 10:31:42 +0900469
David Pursehouse86d973d2012-08-24 10:21:02 +0900470 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900471 username = None
472 password = None
473 if opt.manifest_server_username and opt.manifest_server_password:
474 username = opt.manifest_server_username
475 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900476 else:
477 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900478 info = netrc.netrc()
479 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700480 print('.netrc file does not exist or could not be opened',
481 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900482 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900483 try:
484 parse_result = urlparse.urlparse(manifest_server)
485 if parse_result.hostname:
486 username, _account, password = \
487 info.authenticators(parse_result.hostname)
488 except TypeError:
489 # TypeError is raised when the given hostname is not present
490 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700491 print('No credentials found for %s in .netrc'
492 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700493 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700494 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900495
496 if (username and password):
497 manifest_server = manifest_server.replace('://', '://%s:%s@' %
498 (username, password),
499 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900500
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700501 try:
David Pursehouse86d973d2012-08-24 10:21:02 +0900502 server = xmlrpclib.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200503 if opt.smart_sync:
504 p = self.manifest.manifestProject
505 b = p.GetBranch(p.CurrentBranch)
506 branch = b.merge
507 if branch.startswith(R_HEADS):
508 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700509
Victor Boivie08c880d2011-04-19 10:32:52 +0200510 env = os.environ.copy()
511 if (env.has_key('TARGET_PRODUCT') and
512 env.has_key('TARGET_BUILD_VARIANT')):
513 target = '%s-%s' % (env['TARGET_PRODUCT'],
514 env['TARGET_BUILD_VARIANT'])
515 [success, manifest_str] = server.GetApprovedManifest(branch, target)
516 else:
517 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700518 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200519 assert(opt.smart_tag)
520 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700521
522 if success:
523 manifest_name = "smart_sync_override.xml"
524 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
525 manifest_name)
526 try:
527 f = open(manifest_path, 'w')
528 try:
529 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700530 finally:
531 f.close()
532 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700533 print('error: cannot write manifest to %s' % manifest_path,
534 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700535 sys.exit(1)
Nico Sallembien719965a2010-04-20 15:28:19 -0700536 self.manifest.Override(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700537 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700538 print('error: %s' % manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700539 sys.exit(1)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700540 except (socket.error, IOError, xmlrpclib.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700541 print('error: cannot connect to manifest server %s:\n%s'
542 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900543 sys.exit(1)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700544 except xmlrpclib.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700545 print('error: cannot connect to manifest server %s:\n%d %s'
546 % (self.manifest.manifest_server, e.errcode, e.errmsg),
547 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700548 sys.exit(1)
549
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700550 rp = self.manifest.repoProject
551 rp.PreSync()
552
553 mp = self.manifest.manifestProject
554 mp.PreSync()
555
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800556 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700557 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800558
Nico Sallembien9bb18162009-12-07 15:38:01 -0800559 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700560 mp.Sync_NetworkHalf(quiet=opt.quiet,
561 current_branch_only=opt.current_branch_only)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800562
563 if mp.HasChanges:
564 syncbuf = SyncBuffer(mp.config)
565 mp.Sync_LocalHalf(syncbuf)
566 if not syncbuf.Finish():
567 sys.exit(1)
568 self.manifest._Unload()
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700569 if opt.jobs is None:
570 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800571 all_projects = self.GetProjects(args,
572 missing_ok=True,
573 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700574
Dave Borowitz67700e92012-10-23 15:00:54 -0700575 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700576 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700577 to_fetch = []
578 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700579 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700580 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900581 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700582 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700583
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800584 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700585 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700586 if opt.network_only:
587 # bail out now; the rest touches the working tree
588 return
589
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800590 # Iteratively fetch missing and/or nested unregistered submodules
591 previously_missing_set = set()
592 while True:
593 self.manifest._Unload()
594 all_projects = self.GetProjects(args,
595 missing_ok=True,
596 submodules_ok=opt.fetch_submodules)
597 missing = []
598 for project in all_projects:
599 if project.gitdir not in fetched:
600 missing.append(project)
601 if not missing:
602 break
603 # Stop us from non-stopped fetching actually-missing repos: If set of
604 # missing repos has not been changed from last fetch, we break.
605 missing_set = set(p.name for p in missing)
606 if previously_missing_set == missing_set:
607 break
608 previously_missing_set = missing_set
609 fetched.update(self._Fetch(missing, opt))
610
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700611 if self.manifest.IsMirror:
612 # bail out now, we have no working tree
613 return
614
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700615 if self.UpdateProjectList():
616 sys.exit(1)
617
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700618 syncbuf = SyncBuffer(mp.config,
619 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900620 pm = Progress('Syncing work tree', len(all_projects))
621 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700622 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800623 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700624 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700625 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700626 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700627 if not syncbuf.Finish():
628 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700629
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700630 # If there's a notice that's supposed to print at the end of the sync, print
631 # it now...
632 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700633 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700634
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700635def _PostRepoUpgrade(manifest, quiet=False):
Conley Owensc9129d92012-10-01 16:12:28 -0700636 wrapper = WrapperModule()
637 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700638 wrapper.SetupGnuPG(quiet)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700639 for project in manifest.projects.values():
640 if project.Exists:
641 project.PostRepoUpgrade()
642
643def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
644 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700645 print('info: A new version of repo is available', file=sys.stderr)
646 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700647 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700648 syncbuf = SyncBuffer(rp.config)
649 rp.Sync_LocalHalf(syncbuf)
650 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700651 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700652 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700653 raise RepoChangedException(['--repo-upgraded'])
654 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700655 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700656 else:
657 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700658 print('repo version %s is current' % rp.work_git.describe(HEAD),
659 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700660
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700661def _VerifyTag(project):
662 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
663 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700664 print('warning: GnuPG was not available during last "repo init"\n'
665 'warning: Cannot automatically authenticate repo."""',
666 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700667 return True
668
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700669 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700670 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700671 except GitError:
672 cur = None
673
674 if not cur \
675 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700676 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700677 if rev.startswith(R_HEADS):
678 rev = rev[len(R_HEADS):]
679
Sarah Owenscecd1d82012-11-01 22:59:27 -0700680 print(file=sys.stderr)
681 print("warning: project '%s' branch '%s' is not signed"
682 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700683 return False
684
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800685 env = os.environ.copy()
686 env['GIT_DIR'] = project.gitdir.encode()
687 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700688
689 cmd = [GIT, 'tag', '-v', cur]
690 proc = subprocess.Popen(cmd,
691 stdout = subprocess.PIPE,
692 stderr = subprocess.PIPE,
693 env = env)
694 out = proc.stdout.read()
695 proc.stdout.close()
696
697 err = proc.stderr.read()
698 proc.stderr.close()
699
700 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700701 print(file=sys.stderr)
702 print(out, file=sys.stderr)
703 print(err, file=sys.stderr)
704 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700705 return False
706 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700707
708class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700709 _ALPHA = 0.5
710
Dave Borowitz67700e92012-10-23 15:00:54 -0700711 def __init__(self, manifest):
712 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
713 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700714 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700715
716 def Get(self, project):
717 self._Load()
718 return self._times.get(project.name, _ONE_DAY_S)
719
720 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700721 self._Load()
722 name = project.name
723 old = self._times.get(name, t)
724 self._seen.add(name)
725 a = self._ALPHA
726 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700727
728 def _Load(self):
729 if self._times is None:
730 try:
731 f = open(self._path)
732 except IOError:
733 self._times = {}
734 return self._times
735 try:
736 try:
737 self._times = pickle.load(f)
David Pursehouse1d947b32012-10-25 12:23:11 +0900738 except IOError:
Dave Borowitz67700e92012-10-23 15:00:54 -0700739 try:
740 os.remove(self._path)
741 except OSError:
742 pass
743 self._times = {}
744 finally:
745 f.close()
746 return self._times
747
748 def Save(self):
749 if self._times is None:
750 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700751
752 to_delete = []
753 for name in self._times:
754 if name not in self._seen:
755 to_delete.append(name)
756 for name in to_delete:
757 del self._times[name]
758
Dave Borowitz67700e92012-10-23 15:00:54 -0700759 try:
760 f = open(self._path, 'wb')
761 try:
762 pickle.dump(self._times, f)
763 except (IOError, OSError, pickle.PickleError):
764 try:
765 os.remove(self._path)
766 except OSError:
767 pass
768 finally:
769 f.close()