blob: 0279ff6050790e13ea1ac7967eabf6a9cbfdbed6 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
David Pursehouse86d973d2012-08-24 10:21:02 +090017import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070018from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Dave Borowitz67700e92012-10-23 15:00:54 -070020import pickle
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070022import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Shawn O. Pearcef6906872009-04-18 10:49:00 -070026import time
David Pursehouse59bbb582013-05-17 10:49:33 +090027
28from pyversion import is_python3
29if is_python3():
Chirayu Desai217ea7d2013-03-01 19:14:38 +053030 import urllib.parse
David Pursehouse59bbb582013-05-17 10:49:33 +090031 import xmlrpc.client
32else:
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import imp
34 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053036 urllib = imp.new_module('urllib')
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053037 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 xmlrpc = imp.new_module('xmlrpc')
39 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Roy Lee18afd7f2010-05-09 04:32:08 +080041try:
42 import threading as _threading
43except ImportError:
44 import dummy_threading as _threading
45
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070046try:
47 import resource
48 def _rlimit_nofile():
49 return resource.getrlimit(resource.RLIMIT_NOFILE)
50except ImportError:
51 def _rlimit_nofile():
52 return (256, 256)
53
Dave Borowitz18857212012-10-23 17:02:59 -070054try:
55 import multiprocessing
56except ImportError:
57 multiprocessing = None
58
Dave Borowitze2152672012-10-31 12:24:38 -070059from git_command import GIT, git_require
David Pursehoused94aaef2012-09-07 09:52:04 +090060from git_refs import R_HEADS, HEAD
Conley Owensc9129d92012-10-01 16:12:28 -070061from main import WrapperModule
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070062from project import Project
63from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080064from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000065from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070066from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070067from progress import Progress
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068
Dave Borowitz67700e92012-10-23 15:00:54 -070069_ONE_DAY_S = 24 * 60 * 60
70
Doug Andersonfc06ced2011-03-16 15:49:18 -070071class _FetchError(Exception):
72 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
73 pass
74
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080075class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080076 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070077 common = True
78 helpSummary = "Update working tree to the latest revision"
79 helpUsage = """
80%prog [<project>...]
81"""
82 helpDescription = """
83The '%prog' command synchronizes local project directories
84with the remote repositories specified in the manifest. If a local
85project does not yet exist, it will clone a new local directory from
86the remote repository and set up tracking branches as specified in
87the manifest. If the local project already exists, '%prog'
88will update the remote branches and rebase any new local changes
89on top of the new remote changes.
90
91'%prog' will synchronize all projects listed at the command
92line. Projects can be specified either by name, or by a relative
93or absolute path to the project's local directory. If no projects
94are specified, '%prog' will synchronize all projects listed in
95the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070096
97The -d/--detach option can be used to switch specified projects
98back to the manifest revision. This option is especially helpful
99if the project is currently on a topic branch, but the manifest
100revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700101
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700102The -s/--smart-sync option can be used to sync to a known good
103build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200104manifest. The -t/--smart-tag option is similar and allows you to
105specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700106
David Pursehousecf76b1b2012-09-14 10:31:42 +0900107The -u/--manifest-server-username and -p/--manifest-server-password
108options can be used to specify a username and password to authenticate
109with the manifest server when using the -s or -t option.
110
111If -u and -p are not specified when using the -s or -t option, '%prog'
112will attempt to read authentication credentials for the manifest server
113from the user's .netrc file.
114
115'%prog' will not use authentication credentials from -u/-p or .netrc
116if the manifest server specified in the manifest file already includes
117credentials.
118
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500119The -f/--force-broken option can be used to proceed with syncing
120other projects if a project sync fails.
121
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700122The --no-clone-bundle option disables any attempt to use
123$URL/clone.bundle to bootstrap a new Git repository from a
124resumeable bundle file on a content delivery network. This
125may be necessary if there are problems with the local Python
126HTTP client or proxy configuration, but the Git binary works.
127
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800128The --fetch-submodules option enables fetching Git submodules
129of a project from server.
130
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700131SSH Connections
132---------------
133
134If at least one project remote URL uses an SSH connection (ssh://,
135git+ssh://, or user@host:path syntax) repo will automatically
136enable the SSH ControlMaster option when connecting to that host.
137This feature permits other projects in the same '%prog' session to
138reuse the same SSH tunnel, saving connection setup overheads.
139
140To disable this behavior on UNIX platforms, set the GIT_SSH
141environment variable to 'ssh'. For example:
142
143 export GIT_SSH=ssh
144 %prog
145
146Compatibility
147~~~~~~~~~~~~~
148
149This feature is automatically disabled on Windows, due to the lack
150of UNIX domain socket support.
151
152This feature is not compatible with url.insteadof rewrites in the
153user's ~/.gitconfig. '%prog' is currently not able to perform the
154rewrite early enough to establish the ControlMaster tunnel.
155
156If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
157later is required to fix a server side protocol bug.
158
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159"""
160
Nico Sallembien6623b212010-05-11 12:57:01 -0700161 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000162 try:
163 self.jobs = self.manifest.default.sync_j
164 except ManifestParseError:
165 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700166
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500167 p.add_option('-f', '--force-broken',
168 dest='force_broken', action='store_true',
169 help="continue sync even if a project fails to sync")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900170 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700171 dest='local_only', action='store_true',
172 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900173 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700174 dest='network_only', action='store_true',
175 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900176 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700177 dest='detach_head', action='store_true',
178 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900179 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700180 dest='current_branch_only', action='store_true',
181 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900182 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700183 dest='quiet', action='store_true',
184 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900185 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800186 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700187 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500188 p.add_option('-m', '--manifest-name',
189 dest='manifest_name',
190 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700191 p.add_option('--no-clone-bundle',
192 dest='no_clone_bundle', action='store_true',
193 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800194 p.add_option('-u', '--manifest-server-username', action='store',
195 dest='manifest_server_username',
196 help='username to authenticate with the manifest server')
197 p.add_option('-p', '--manifest-server-password', action='store',
198 dest='manifest_server_password',
199 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800200 p.add_option('--fetch-submodules',
201 dest='fetch_submodules', action='store_true',
202 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700203 p.add_option('--no-tags',
204 dest='no_tags', action='store_true',
205 help="don't fetch tags")
Nico Sallembien6623b212010-05-11 12:57:01 -0700206 if show_smart:
207 p.add_option('-s', '--smart-sync',
208 dest='smart_sync', action='store_true',
209 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200210 p.add_option('-t', '--smart-tag',
211 dest='smart_tag', action='store',
212 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700213
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700214 g = p.add_option_group('repo Version options')
215 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216 dest='no_repo_verify', action='store_true',
217 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700218 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800219 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700220 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700221
Doug Andersonfc06ced2011-03-16 15:49:18 -0700222 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
David Pursehousec1b86a22012-11-14 11:36:51 +0900223 """Main function of the fetch threads when jobs are > 1.
Doug Andersonfc06ced2011-03-16 15:49:18 -0700224
David Pursehousec1b86a22012-11-14 11:36:51 +0900225 Args:
226 opt: Program options returned from optparse. See _Options().
227 project: Project object for the project to fetch.
228 lock: Lock for accessing objects that are shared amongst multiple
229 _FetchHelper() threads.
230 fetched: set object that we will add project.gitdir to when we're done
231 (with our lock held).
232 pm: Instance of a Project object. We will call pm.update() (with our
233 lock held).
234 sem: We'll release() this semaphore when we exit so that another thread
235 can be started up.
236 err_event: We'll set this event in the case of an error (after printing
237 out info about the error).
238 """
239 # We'll set to true once we've locked the lock.
240 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700241
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530242 if not opt.quiet:
243 print('Fetching project %s' % project.name)
244
David Pursehousec1b86a22012-11-14 11:36:51 +0900245 # Encapsulate everything in a try/except/finally so that:
246 # - We always set err_event in the case of an exception.
247 # - We always make sure we call sem.release().
248 # - We always make sure we unlock the lock if we locked it.
249 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700250 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900251 start = time.time()
252 success = project.Sync_NetworkHalf(
253 quiet=opt.quiet,
254 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700255 clone_bundle=not opt.no_clone_bundle,
Julien Campergue335f5ef2013-10-16 11:02:35 +0200256 no_tags=opt.no_tags, archive=self.manifest.IsArchive)
David Pursehousec1b86a22012-11-14 11:36:51 +0900257 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700258
David Pursehousec1b86a22012-11-14 11:36:51 +0900259 # Lock around all the rest of the code, since printing, updating a set
260 # and Progress.update() are not thread safe.
261 lock.acquire()
262 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700263
David Pursehousec1b86a22012-11-14 11:36:51 +0900264 if not success:
265 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
266 if opt.force_broken:
267 print('warn: --force-broken, continuing to sync',
268 file=sys.stderr)
269 else:
270 raise _FetchError()
Roy Lee18afd7f2010-05-09 04:32:08 +0800271
David Pursehousec1b86a22012-11-14 11:36:51 +0900272 fetched.add(project.gitdir)
273 pm.update()
274 except _FetchError:
275 err_event.set()
276 except:
277 err_event.set()
278 raise
279 finally:
280 if did_lock:
281 lock.release()
282 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800283
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700284 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700285 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700286 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800287
288 if self.jobs == 1:
289 for project in projects:
290 pm.update()
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530291 if not opt.quiet:
292 print('Fetching project %s' % project.name)
Shawn O. Pearce5d0efdb2012-08-02 12:13:01 -0700293 if project.Sync_NetworkHalf(
294 quiet=opt.quiet,
295 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700296 clone_bundle=not opt.no_clone_bundle,
Julien Campergue335f5ef2013-10-16 11:02:35 +0200297 no_tags=opt.no_tags,
298 archive=self.manifest.IsArchive):
Roy Lee18afd7f2010-05-09 04:32:08 +0800299 fetched.add(project.gitdir)
300 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700301 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500302 if opt.force_broken:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700303 print('warn: --force-broken, continuing to sync', file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500304 else:
305 sys.exit(1)
Roy Lee18afd7f2010-05-09 04:32:08 +0800306 else:
307 threads = set()
308 lock = _threading.Lock()
309 sem = _threading.Semaphore(self.jobs)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700310 err_event = _threading.Event()
Roy Lee18afd7f2010-05-09 04:32:08 +0800311 for project in projects:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700312 # Check for any errors before starting any new threads.
313 # ...we'll let existing threads finish, though.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400314 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700315 break
316
Roy Lee18afd7f2010-05-09 04:32:08 +0800317 sem.acquire()
318 t = _threading.Thread(target = self._FetchHelper,
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700319 args = (opt,
320 project,
321 lock,
322 fetched,
323 pm,
Doug Andersonfc06ced2011-03-16 15:49:18 -0700324 sem,
325 err_event))
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200326 # Ensure that Ctrl-C will not freeze the repo process.
327 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800328 threads.add(t)
329 t.start()
330
331 for t in threads:
332 t.join()
333
Doug Andersonfc06ced2011-03-16 15:49:18 -0700334 # If we saw an error, exit with code 1 so that other scripts can check.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400335 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700336 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700337 sys.exit(1)
338
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700339 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700340 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700341
Julien Campergue335f5ef2013-10-16 11:02:35 +0200342 if not self.manifest.IsArchive:
343 self._GCProjects(projects)
344
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700345 return fetched
346
Dave Borowitz18857212012-10-23 17:02:59 -0700347 def _GCProjects(self, projects):
Dave Borowitze2152672012-10-31 12:24:38 -0700348 has_dash_c = git_require((1, 7, 2))
349 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700350 cpu_count = multiprocessing.cpu_count()
351 else:
352 cpu_count = 1
353 jobs = min(self.jobs, cpu_count)
354
355 if jobs < 2:
356 for project in projects:
357 project.bare_git.gc('--auto')
358 return
359
360 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
361
362 threads = set()
363 sem = _threading.Semaphore(jobs)
364 err_event = _threading.Event()
365
366 def GC(project):
367 try:
368 try:
369 project.bare_git.gc('--auto', config=config)
370 except GitError:
371 err_event.set()
372 except:
373 err_event.set()
374 raise
375 finally:
376 sem.release()
377
378 for project in projects:
379 if err_event.isSet():
380 break
381 sem.acquire()
382 t = _threading.Thread(target=GC, args=(project,))
383 t.daemon = True
384 threads.add(t)
385 t.start()
386
387 for t in threads:
388 t.join()
389
390 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700391 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700392 sys.exit(1)
393
Tim Kilbourn07669002013-03-08 15:02:49 -0800394 def _ReloadManifest(self, manifest_name=None):
395 if manifest_name:
396 # Override calls _Unload already
397 self.manifest.Override(manifest_name)
398 else:
399 self.manifest._Unload()
400
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700401 def UpdateProjectList(self):
402 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700403 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700404 if project.relpath:
405 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700406 file_name = 'project.list'
407 file_path = os.path.join(self.manifest.repodir, file_name)
408 old_project_paths = []
409
410 if os.path.exists(file_path):
411 fd = open(file_path, 'r')
412 try:
413 old_project_paths = fd.read().split('\n')
414 finally:
415 fd.close()
416 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700417 if not path:
418 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700419 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900420 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400421 if os.path.exists(self.manifest.topdir + '/' + path):
David Pursehousec1b86a22012-11-14 11:36:51 +0900422 project = Project(
423 manifest = self.manifest,
424 name = path,
425 remote = RemoteSpec('origin'),
426 gitdir = os.path.join(self.manifest.topdir,
427 path, '.git'),
428 worktree = os.path.join(self.manifest.topdir, path),
429 relpath = path,
430 revisionExpr = 'HEAD',
431 revisionId = None,
432 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400433
David Pursehousec1b86a22012-11-14 11:36:51 +0900434 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900435 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900436 'are present' % project.relpath, file=sys.stderr)
437 print(' commit changes, then run sync again',
438 file=sys.stderr)
439 return -1
440 else:
441 print('Deleting obsolete path %s' % project.worktree,
442 file=sys.stderr)
443 shutil.rmtree(project.worktree)
444 # Try deleting parent subdirs if they are empty
445 project_dir = os.path.dirname(project.worktree)
446 while project_dir != self.manifest.topdir:
447 try:
448 os.rmdir(project_dir)
449 except OSError:
450 break
451 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700452
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700453 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700454 fd = open(file_path, 'w')
455 try:
456 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700457 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700458 finally:
459 fd.close()
460 return 0
461
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700462 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800463 if opt.jobs:
464 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700465 if self.jobs > 1:
466 soft_limit, _ = _rlimit_nofile()
467 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
468
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700469 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700470 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700471 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700472 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700473 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700474 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500475 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700476 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500477 sys.exit(1)
478 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700479 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500480 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900481 if opt.manifest_server_username or opt.manifest_server_password:
482 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700483 print('error: -u and -p may only be combined with -s or -t',
484 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900485 sys.exit(1)
486 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700487 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900488 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500489
490 if opt.manifest_name:
491 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700492
Chirayu Desaia892b102013-06-11 14:18:46 +0530493 manifest_name = opt.manifest_name
494
Victor Boivie08c880d2011-04-19 10:32:52 +0200495 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700496 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900497 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700498 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700499 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900500
501 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900502 if not opt.quiet:
503 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900504
David Pursehouse86d973d2012-08-24 10:21:02 +0900505 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900506 username = None
507 password = None
508 if opt.manifest_server_username and opt.manifest_server_password:
509 username = opt.manifest_server_username
510 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900511 else:
512 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900513 info = netrc.netrc()
514 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700515 print('.netrc file does not exist or could not be opened',
516 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900517 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900518 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530519 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900520 if parse_result.hostname:
521 username, _account, password = \
522 info.authenticators(parse_result.hostname)
523 except TypeError:
524 # TypeError is raised when the given hostname is not present
525 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700526 print('No credentials found for %s in .netrc'
527 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700528 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700529 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900530
531 if (username and password):
532 manifest_server = manifest_server.replace('://', '://%s:%s@' %
533 (username, password),
534 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900535
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700536 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530537 server = xmlrpc.client.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200538 if opt.smart_sync:
539 p = self.manifest.manifestProject
540 b = p.GetBranch(p.CurrentBranch)
541 branch = b.merge
542 if branch.startswith(R_HEADS):
543 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700544
Victor Boivie08c880d2011-04-19 10:32:52 +0200545 env = os.environ.copy()
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530546 if 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200547 target = '%s-%s' % (env['TARGET_PRODUCT'],
548 env['TARGET_BUILD_VARIANT'])
549 [success, manifest_str] = server.GetApprovedManifest(branch, target)
550 else:
551 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700552 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200553 assert(opt.smart_tag)
554 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700555
556 if success:
557 manifest_name = "smart_sync_override.xml"
558 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
559 manifest_name)
560 try:
561 f = open(manifest_path, 'w')
562 try:
563 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700564 finally:
565 f.close()
566 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700567 print('error: cannot write manifest to %s' % manifest_path,
568 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700569 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100570 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700571 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900572 print('error: manifest server RPC call failed: %s' %
573 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700574 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530575 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700576 print('error: cannot connect to manifest server %s:\n%s'
577 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900578 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530579 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700580 print('error: cannot connect to manifest server %s:\n%d %s'
581 % (self.manifest.manifest_server, e.errcode, e.errmsg),
582 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700583 sys.exit(1)
584
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700585 rp = self.manifest.repoProject
586 rp.PreSync()
587
588 mp = self.manifest.manifestProject
589 mp.PreSync()
590
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800591 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700592 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800593
Nico Sallembien9bb18162009-12-07 15:38:01 -0800594 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700595 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700596 current_branch_only=opt.current_branch_only,
597 no_tags=opt.no_tags)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800598
599 if mp.HasChanges:
600 syncbuf = SyncBuffer(mp.config)
601 mp.Sync_LocalHalf(syncbuf)
602 if not syncbuf.Finish():
603 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100604 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700605 if opt.jobs is None:
606 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800607 all_projects = self.GetProjects(args,
608 missing_ok=True,
609 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700610
Dave Borowitz67700e92012-10-23 15:00:54 -0700611 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700612 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700613 to_fetch = []
614 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700615 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700616 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900617 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700618 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700619
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800620 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700621 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700622 if opt.network_only:
623 # bail out now; the rest touches the working tree
624 return
625
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800626 # Iteratively fetch missing and/or nested unregistered submodules
627 previously_missing_set = set()
628 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100629 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800630 all_projects = self.GetProjects(args,
631 missing_ok=True,
632 submodules_ok=opt.fetch_submodules)
633 missing = []
634 for project in all_projects:
635 if project.gitdir not in fetched:
636 missing.append(project)
637 if not missing:
638 break
639 # Stop us from non-stopped fetching actually-missing repos: If set of
640 # missing repos has not been changed from last fetch, we break.
641 missing_set = set(p.name for p in missing)
642 if previously_missing_set == missing_set:
643 break
644 previously_missing_set = missing_set
645 fetched.update(self._Fetch(missing, opt))
646
Julien Campergue335f5ef2013-10-16 11:02:35 +0200647 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700648 # bail out now, we have no working tree
649 return
650
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700651 if self.UpdateProjectList():
652 sys.exit(1)
653
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700654 syncbuf = SyncBuffer(mp.config,
655 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900656 pm = Progress('Syncing work tree', len(all_projects))
657 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700658 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800659 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700660 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700661 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700662 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700663 if not syncbuf.Finish():
664 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700665
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700666 # If there's a notice that's supposed to print at the end of the sync, print
667 # it now...
668 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700669 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700670
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700671def _PostRepoUpgrade(manifest, quiet=False):
Conley Owensc9129d92012-10-01 16:12:28 -0700672 wrapper = WrapperModule()
673 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700674 wrapper.SetupGnuPG(quiet)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700675 for project in manifest.projects.values():
676 if project.Exists:
677 project.PostRepoUpgrade()
678
679def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
680 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700681 print('info: A new version of repo is available', file=sys.stderr)
682 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700683 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700684 syncbuf = SyncBuffer(rp.config)
685 rp.Sync_LocalHalf(syncbuf)
686 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700687 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700688 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700689 raise RepoChangedException(['--repo-upgraded'])
690 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700691 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700692 else:
693 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700694 print('repo version %s is current' % rp.work_git.describe(HEAD),
695 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700696
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700697def _VerifyTag(project):
698 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
699 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700700 print('warning: GnuPG was not available during last "repo init"\n'
701 'warning: Cannot automatically authenticate repo."""',
702 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700703 return True
704
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700705 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700706 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700707 except GitError:
708 cur = None
709
710 if not cur \
711 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700712 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700713 if rev.startswith(R_HEADS):
714 rev = rev[len(R_HEADS):]
715
Sarah Owenscecd1d82012-11-01 22:59:27 -0700716 print(file=sys.stderr)
717 print("warning: project '%s' branch '%s' is not signed"
718 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700719 return False
720
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800721 env = os.environ.copy()
722 env['GIT_DIR'] = project.gitdir.encode()
723 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700724
725 cmd = [GIT, 'tag', '-v', cur]
726 proc = subprocess.Popen(cmd,
727 stdout = subprocess.PIPE,
728 stderr = subprocess.PIPE,
729 env = env)
730 out = proc.stdout.read()
731 proc.stdout.close()
732
733 err = proc.stderr.read()
734 proc.stderr.close()
735
736 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700737 print(file=sys.stderr)
738 print(out, file=sys.stderr)
739 print(err, file=sys.stderr)
740 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700741 return False
742 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700743
744class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700745 _ALPHA = 0.5
746
Dave Borowitz67700e92012-10-23 15:00:54 -0700747 def __init__(self, manifest):
748 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
749 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700750 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700751
752 def Get(self, project):
753 self._Load()
754 return self._times.get(project.name, _ONE_DAY_S)
755
756 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700757 self._Load()
758 name = project.name
759 old = self._times.get(name, t)
760 self._seen.add(name)
761 a = self._ALPHA
762 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700763
764 def _Load(self):
765 if self._times is None:
766 try:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +0530767 f = open(self._path, 'rb')
Dave Borowitz67700e92012-10-23 15:00:54 -0700768 except IOError:
769 self._times = {}
770 return self._times
771 try:
772 try:
773 self._times = pickle.load(f)
David Pursehouse1d947b32012-10-25 12:23:11 +0900774 except IOError:
Dave Borowitz67700e92012-10-23 15:00:54 -0700775 try:
776 os.remove(self._path)
777 except OSError:
778 pass
779 self._times = {}
780 finally:
781 f.close()
782 return self._times
783
784 def Save(self):
785 if self._times is None:
786 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700787
788 to_delete = []
789 for name in self._times:
790 if name not in self._seen:
791 to_delete.append(name)
792 for name in to_delete:
793 del self._times[name]
794
Dave Borowitz67700e92012-10-23 15:00:54 -0700795 try:
796 f = open(self._path, 'wb')
797 try:
798 pickle.dump(self._times, f)
799 except (IOError, OSError, pickle.PickleError):
800 try:
801 os.remove(self._path)
802 except OSError:
803 pass
804 finally:
805 f.close()