blob: f80947380d2d7d8703cdf6a9351efca5dd477244 [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
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070054from error import RepoChangedException, GitError
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):
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700151 self.jobs = self.manifest.default.sync_j
152
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500153 p.add_option('-f', '--force-broken',
154 dest='force_broken', action='store_true',
155 help="continue sync even if a project fails to sync")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900156 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700157 dest='local_only', action='store_true',
158 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900159 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700160 dest='network_only', action='store_true',
161 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900162 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700163 dest='detach_head', action='store_true',
164 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900165 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700166 dest='current_branch_only', action='store_true',
167 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900168 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700169 dest='quiet', action='store_true',
170 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900171 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800172 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700173 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500174 p.add_option('-m', '--manifest-name',
175 dest='manifest_name',
176 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700177 p.add_option('--no-clone-bundle',
178 dest='no_clone_bundle', action='store_true',
179 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800180 p.add_option('-u', '--manifest-server-username', action='store',
181 dest='manifest_server_username',
182 help='username to authenticate with the manifest server')
183 p.add_option('-p', '--manifest-server-password', action='store',
184 dest='manifest_server_password',
185 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800186 p.add_option('--fetch-submodules',
187 dest='fetch_submodules', action='store_true',
188 help='fetch submodules from server')
Nico Sallembien6623b212010-05-11 12:57:01 -0700189 if show_smart:
190 p.add_option('-s', '--smart-sync',
191 dest='smart_sync', action='store_true',
192 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200193 p.add_option('-t', '--smart-tag',
194 dest='smart_tag', action='store',
195 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700196
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700197 g = p.add_option_group('repo Version options')
198 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700199 dest='no_repo_verify', action='store_true',
200 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700201 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800202 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700203 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700204
Doug Andersonfc06ced2011-03-16 15:49:18 -0700205 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
David Pursehousec1b86a22012-11-14 11:36:51 +0900206 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800207
David Pursehousec1b86a22012-11-14 11:36:51 +0900208 Args:
209 opt: Program options returned from optparse. See _Options().
210 project: Project object for the project to fetch.
211 lock: Lock for accessing objects that are shared amongst multiple
212 _FetchHelper() threads.
213 fetched: set object that we will add project.gitdir to when we're done
214 (with our lock held).
215 pm: Instance of a Project object. We will call pm.update() (with our
216 lock held).
217 sem: We'll release() this semaphore when we exit so that another thread
218 can be started up.
219 err_event: We'll set this event in the case of an error (after printing
220 out info about the error).
221 """
222 # We'll set to true once we've locked the lock.
223 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700224
David Pursehousec1b86a22012-11-14 11:36:51 +0900225 # Encapsulate everything in a try/except/finally so that:
226 # - We always set err_event in the case of an exception.
227 # - We always make sure we call sem.release().
228 # - We always make sure we unlock the lock if we locked it.
229 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700230 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900231 start = time.time()
232 success = project.Sync_NetworkHalf(
233 quiet=opt.quiet,
234 current_branch_only=opt.current_branch_only,
235 clone_bundle=not opt.no_clone_bundle)
236 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700237
David Pursehousec1b86a22012-11-14 11:36:51 +0900238 # Lock around all the rest of the code, since printing, updating a set
239 # and Progress.update() are not thread safe.
240 lock.acquire()
241 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700242
David Pursehousec1b86a22012-11-14 11:36:51 +0900243 if not success:
244 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
245 if opt.force_broken:
246 print('warn: --force-broken, continuing to sync',
247 file=sys.stderr)
248 else:
249 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700250
David Pursehousec1b86a22012-11-14 11:36:51 +0900251 fetched.add(project.gitdir)
252 pm.update()
253 except _FetchError:
254 err_event.set()
255 except:
256 err_event.set()
257 raise
258 finally:
259 if did_lock:
260 lock.release()
261 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800262
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700263 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700264 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700265 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800266
267 if self.jobs == 1:
268 for project in projects:
269 pm.update()
Shawn O. Pearce5d0efdb2012-08-02 12:13:01 -0700270 if project.Sync_NetworkHalf(
271 quiet=opt.quiet,
272 current_branch_only=opt.current_branch_only,
273 clone_bundle=not opt.no_clone_bundle):
Roy Lee18afd7f2010-05-09 04:32:08 +0800274 fetched.add(project.gitdir)
275 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700276 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500277 if opt.force_broken:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700278 print('warn: --force-broken, continuing to sync', file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500279 else:
280 sys.exit(1)
Roy Lee18afd7f2010-05-09 04:32:08 +0800281 else:
282 threads = set()
283 lock = _threading.Lock()
284 sem = _threading.Semaphore(self.jobs)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700285 err_event = _threading.Event()
Roy Lee18afd7f2010-05-09 04:32:08 +0800286 for project in projects:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700287 # Check for any errors before starting any new threads.
288 # ...we'll let existing threads finish, though.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400289 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700290 break
291
Roy Lee18afd7f2010-05-09 04:32:08 +0800292 sem.acquire()
293 t = _threading.Thread(target = self._FetchHelper,
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700294 args = (opt,
295 project,
296 lock,
297 fetched,
298 pm,
Doug Andersonfc06ced2011-03-16 15:49:18 -0700299 sem,
300 err_event))
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200301 # Ensure that Ctrl-C will not freeze the repo process.
302 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800303 threads.add(t)
304 t.start()
305
306 for t in threads:
307 t.join()
308
Doug Andersonfc06ced2011-03-16 15:49:18 -0700309 # If we saw an error, exit with code 1 so that other scripts can check.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400310 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700311 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700312 sys.exit(1)
313
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700314 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700315 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700316
317 self._GCProjects(projects)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700318 return fetched
319
Dave Borowitz18857212012-10-23 17:02:59 -0700320 def _GCProjects(self, projects):
Dave Borowitze2152672012-10-31 12:24:38 -0700321 has_dash_c = git_require((1, 7, 2))
322 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700323 cpu_count = multiprocessing.cpu_count()
324 else:
325 cpu_count = 1
326 jobs = min(self.jobs, cpu_count)
327
328 if jobs < 2:
329 for project in projects:
330 project.bare_git.gc('--auto')
331 return
332
333 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
334
335 threads = set()
336 sem = _threading.Semaphore(jobs)
337 err_event = _threading.Event()
338
339 def GC(project):
340 try:
341 try:
342 project.bare_git.gc('--auto', config=config)
343 except GitError:
344 err_event.set()
345 except:
346 err_event.set()
347 raise
348 finally:
349 sem.release()
350
351 for project in projects:
352 if err_event.isSet():
353 break
354 sem.acquire()
355 t = _threading.Thread(target=GC, args=(project,))
356 t.daemon = True
357 threads.add(t)
358 t.start()
359
360 for t in threads:
361 t.join()
362
363 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700364 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700365 sys.exit(1)
366
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700367 def UpdateProjectList(self):
368 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700369 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700370 if project.relpath:
371 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700372 file_name = 'project.list'
373 file_path = os.path.join(self.manifest.repodir, file_name)
374 old_project_paths = []
375
376 if os.path.exists(file_path):
377 fd = open(file_path, 'r')
378 try:
379 old_project_paths = fd.read().split('\n')
380 finally:
381 fd.close()
382 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700383 if not path:
384 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700385 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900386 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400387 if os.path.exists(self.manifest.topdir + '/' + path):
David Pursehousec1b86a22012-11-14 11:36:51 +0900388 project = Project(
389 manifest = self.manifest,
390 name = path,
391 remote = RemoteSpec('origin'),
392 gitdir = os.path.join(self.manifest.topdir,
393 path, '.git'),
394 worktree = os.path.join(self.manifest.topdir, path),
395 relpath = path,
396 revisionExpr = 'HEAD',
397 revisionId = None,
398 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400399
David Pursehousec1b86a22012-11-14 11:36:51 +0900400 if project.IsDirty():
401 print('error: Cannot remove project "%s": uncommitted changes'
402 'are present' % project.relpath, file=sys.stderr)
403 print(' commit changes, then run sync again',
404 file=sys.stderr)
405 return -1
406 else:
407 print('Deleting obsolete path %s' % project.worktree,
408 file=sys.stderr)
409 shutil.rmtree(project.worktree)
410 # Try deleting parent subdirs if they are empty
411 project_dir = os.path.dirname(project.worktree)
412 while project_dir != self.manifest.topdir:
413 try:
414 os.rmdir(project_dir)
415 except OSError:
416 break
417 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700418
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700419 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700420 fd = open(file_path, 'w')
421 try:
422 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700423 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700424 finally:
425 fd.close()
426 return 0
427
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700428 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800429 if opt.jobs:
430 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700431 if self.jobs > 1:
432 soft_limit, _ = _rlimit_nofile()
433 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
434
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700435 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700436 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700437 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700438 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700439 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700440 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500441 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700442 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500443 sys.exit(1)
444 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700445 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500446 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900447 if opt.manifest_server_username or opt.manifest_server_password:
448 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700449 print('error: -u and -p may only be combined with -s or -t',
450 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900451 sys.exit(1)
452 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700453 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900454 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500455
456 if opt.manifest_name:
457 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700458
Victor Boivie08c880d2011-04-19 10:32:52 +0200459 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700460 if not self.manifest.manifest_server:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700461 print('error: cannot smart sync: no manifest server defined in'
462 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700463 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900464
465 manifest_server = self.manifest.manifest_server
David Pursehousecf76b1b2012-09-14 10:31:42 +0900466
David Pursehouse86d973d2012-08-24 10:21:02 +0900467 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900468 username = None
469 password = None
470 if opt.manifest_server_username and opt.manifest_server_password:
471 username = opt.manifest_server_username
472 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900473 else:
474 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900475 info = netrc.netrc()
476 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700477 print('.netrc file does not exist or could not be opened',
478 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900479 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900480 try:
481 parse_result = urlparse.urlparse(manifest_server)
482 if parse_result.hostname:
483 username, _account, password = \
484 info.authenticators(parse_result.hostname)
485 except TypeError:
486 # TypeError is raised when the given hostname is not present
487 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700488 print('No credentials found for %s in .netrc'
489 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700490 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700491 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900492
493 if (username and password):
494 manifest_server = manifest_server.replace('://', '://%s:%s@' %
495 (username, password),
496 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900497
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700498 try:
David Pursehouse86d973d2012-08-24 10:21:02 +0900499 server = xmlrpclib.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200500 if opt.smart_sync:
501 p = self.manifest.manifestProject
502 b = p.GetBranch(p.CurrentBranch)
503 branch = b.merge
504 if branch.startswith(R_HEADS):
505 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700506
Victor Boivie08c880d2011-04-19 10:32:52 +0200507 env = os.environ.copy()
508 if (env.has_key('TARGET_PRODUCT') and
509 env.has_key('TARGET_BUILD_VARIANT')):
510 target = '%s-%s' % (env['TARGET_PRODUCT'],
511 env['TARGET_BUILD_VARIANT'])
512 [success, manifest_str] = server.GetApprovedManifest(branch, target)
513 else:
514 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700515 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200516 assert(opt.smart_tag)
517 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700518
519 if success:
520 manifest_name = "smart_sync_override.xml"
521 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
522 manifest_name)
523 try:
524 f = open(manifest_path, 'w')
525 try:
526 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700527 finally:
528 f.close()
529 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700530 print('error: cannot write manifest to %s' % manifest_path,
531 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700532 sys.exit(1)
Nico Sallembien719965a2010-04-20 15:28:19 -0700533 self.manifest.Override(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700534 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700535 print('error: %s' % manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700536 sys.exit(1)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700537 except (socket.error, IOError, xmlrpclib.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700538 print('error: cannot connect to manifest server %s:\n%s'
539 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900540 sys.exit(1)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700541 except xmlrpclib.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700542 print('error: cannot connect to manifest server %s:\n%d %s'
543 % (self.manifest.manifest_server, e.errcode, e.errmsg),
544 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700545 sys.exit(1)
546
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700547 rp = self.manifest.repoProject
548 rp.PreSync()
549
550 mp = self.manifest.manifestProject
551 mp.PreSync()
552
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800553 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700554 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800555
Nico Sallembien9bb18162009-12-07 15:38:01 -0800556 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700557 mp.Sync_NetworkHalf(quiet=opt.quiet,
558 current_branch_only=opt.current_branch_only)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800559
560 if mp.HasChanges:
561 syncbuf = SyncBuffer(mp.config)
562 mp.Sync_LocalHalf(syncbuf)
563 if not syncbuf.Finish():
564 sys.exit(1)
565 self.manifest._Unload()
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700566 if opt.jobs is None:
567 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800568 all_projects = self.GetProjects(args,
569 missing_ok=True,
570 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700571
Dave Borowitz67700e92012-10-23 15:00:54 -0700572 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700573 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700574 to_fetch = []
575 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700576 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700577 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900578 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700579 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700580
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800581 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700582 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700583 if opt.network_only:
584 # bail out now; the rest touches the working tree
585 return
586
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800587 # Iteratively fetch missing and/or nested unregistered submodules
588 previously_missing_set = set()
589 while True:
590 self.manifest._Unload()
591 all_projects = self.GetProjects(args,
592 missing_ok=True,
593 submodules_ok=opt.fetch_submodules)
594 missing = []
595 for project in all_projects:
596 if project.gitdir not in fetched:
597 missing.append(project)
598 if not missing:
599 break
600 # Stop us from non-stopped fetching actually-missing repos: If set of
601 # missing repos has not been changed from last fetch, we break.
602 missing_set = set(p.name for p in missing)
603 if previously_missing_set == missing_set:
604 break
605 previously_missing_set = missing_set
606 fetched.update(self._Fetch(missing, opt))
607
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700608 if self.manifest.IsMirror:
609 # bail out now, we have no working tree
610 return
611
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700612 if self.UpdateProjectList():
613 sys.exit(1)
614
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700615 syncbuf = SyncBuffer(mp.config,
616 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900617 pm = Progress('Syncing work tree', len(all_projects))
618 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700619 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800620 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700621 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700622 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700623 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700624 if not syncbuf.Finish():
625 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700626
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700627 # If there's a notice that's supposed to print at the end of the sync, print
628 # it now...
629 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700630 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700631
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700632def _PostRepoUpgrade(manifest, quiet=False):
Conley Owensc9129d92012-10-01 16:12:28 -0700633 wrapper = WrapperModule()
634 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700635 wrapper.SetupGnuPG(quiet)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700636 for project in manifest.projects.values():
637 if project.Exists:
638 project.PostRepoUpgrade()
639
640def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
641 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700642 print('info: A new version of repo is available', file=sys.stderr)
643 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700644 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700645 syncbuf = SyncBuffer(rp.config)
646 rp.Sync_LocalHalf(syncbuf)
647 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700648 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700649 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700650 raise RepoChangedException(['--repo-upgraded'])
651 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700652 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700653 else:
654 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700655 print('repo version %s is current' % rp.work_git.describe(HEAD),
656 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700657
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700658def _VerifyTag(project):
659 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
660 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700661 print('warning: GnuPG was not available during last "repo init"\n'
662 'warning: Cannot automatically authenticate repo."""',
663 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700664 return True
665
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700666 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700667 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700668 except GitError:
669 cur = None
670
671 if not cur \
672 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700673 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700674 if rev.startswith(R_HEADS):
675 rev = rev[len(R_HEADS):]
676
Sarah Owenscecd1d82012-11-01 22:59:27 -0700677 print(file=sys.stderr)
678 print("warning: project '%s' branch '%s' is not signed"
679 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700680 return False
681
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800682 env = os.environ.copy()
683 env['GIT_DIR'] = project.gitdir.encode()
684 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700685
686 cmd = [GIT, 'tag', '-v', cur]
687 proc = subprocess.Popen(cmd,
688 stdout = subprocess.PIPE,
689 stderr = subprocess.PIPE,
690 env = env)
691 out = proc.stdout.read()
692 proc.stdout.close()
693
694 err = proc.stderr.read()
695 proc.stderr.close()
696
697 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700698 print(file=sys.stderr)
699 print(out, file=sys.stderr)
700 print(err, file=sys.stderr)
701 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700702 return False
703 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700704
705class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700706 _ALPHA = 0.5
707
Dave Borowitz67700e92012-10-23 15:00:54 -0700708 def __init__(self, manifest):
709 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
710 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700711 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700712
713 def Get(self, project):
714 self._Load()
715 return self._times.get(project.name, _ONE_DAY_S)
716
717 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700718 self._Load()
719 name = project.name
720 old = self._times.get(name, t)
721 self._seen.add(name)
722 a = self._ALPHA
723 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700724
725 def _Load(self):
726 if self._times is None:
727 try:
728 f = open(self._path)
729 except IOError:
730 self._times = {}
731 return self._times
732 try:
733 try:
734 self._times = pickle.load(f)
David Pursehouse1d947b32012-10-25 12:23:11 +0900735 except IOError:
Dave Borowitz67700e92012-10-23 15:00:54 -0700736 try:
737 os.remove(self._path)
738 except OSError:
739 pass
740 self._times = {}
741 finally:
742 f.close()
743 return self._times
744
745 def Save(self):
746 if self._times is None:
747 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700748
749 to_delete = []
750 for name in self._times:
751 if name not in self._seen:
752 to_delete.append(name)
753 for name in to_delete:
754 del self._times[name]
755
Dave Borowitz67700e92012-10-23 15:00:54 -0700756 try:
757 f = open(self._path, 'wb')
758 try:
759 pickle.dump(self._times, f)
760 except (IOError, OSError, pickle.PickleError):
761 try:
762 os.remove(self._path)
763 except OSError:
764 pass
765 finally:
766 f.close()