blob: 2bdab3a6eb77a65a3d3a951564324760a48a9146 [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
Anthony King85b24ac2014-05-06 15:57:48 +010017import json
David Pursehouse86d973d2012-08-24 10:21:02 +090018import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070019from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import os
21import 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
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070061from project import Project
62from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080063from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000064from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070065from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070066from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080067from wrapper import Wrapper
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
David Pursehousef2fad612015-01-29 14:36:28 +0900131The -c/--current-branch option can be used to only fetch objects that
132are on the branch specified by a project's revision.
133
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700134SSH Connections
135---------------
136
137If at least one project remote URL uses an SSH connection (ssh://,
138git+ssh://, or user@host:path syntax) repo will automatically
139enable the SSH ControlMaster option when connecting to that host.
140This feature permits other projects in the same '%prog' session to
141reuse the same SSH tunnel, saving connection setup overheads.
142
143To disable this behavior on UNIX platforms, set the GIT_SSH
144environment variable to 'ssh'. For example:
145
146 export GIT_SSH=ssh
147 %prog
148
149Compatibility
150~~~~~~~~~~~~~
151
152This feature is automatically disabled on Windows, due to the lack
153of UNIX domain socket support.
154
155This feature is not compatible with url.insteadof rewrites in the
156user's ~/.gitconfig. '%prog' is currently not able to perform the
157rewrite early enough to establish the ControlMaster tunnel.
158
159If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
160later is required to fix a server side protocol bug.
161
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700162"""
163
Nico Sallembien6623b212010-05-11 12:57:01 -0700164 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000165 try:
166 self.jobs = self.manifest.default.sync_j
167 except ManifestParseError:
168 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700169
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500170 p.add_option('-f', '--force-broken',
171 dest='force_broken', action='store_true',
172 help="continue sync even if a project fails to sync")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900173 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700174 dest='local_only', action='store_true',
175 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900176 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700177 dest='network_only', action='store_true',
178 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900179 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700180 dest='detach_head', action='store_true',
181 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900182 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700183 dest='current_branch_only', action='store_true',
184 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900185 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700186 dest='quiet', action='store_true',
187 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900188 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800189 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700190 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500191 p.add_option('-m', '--manifest-name',
192 dest='manifest_name',
193 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700194 p.add_option('--no-clone-bundle',
195 dest='no_clone_bundle', action='store_true',
196 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800197 p.add_option('-u', '--manifest-server-username', action='store',
198 dest='manifest_server_username',
199 help='username to authenticate with the manifest server')
200 p.add_option('-p', '--manifest-server-password', action='store',
201 dest='manifest_server_password',
202 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800203 p.add_option('--fetch-submodules',
204 dest='fetch_submodules', action='store_true',
205 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700206 p.add_option('--no-tags',
207 dest='no_tags', action='store_true',
208 help="don't fetch tags")
Nico Sallembien6623b212010-05-11 12:57:01 -0700209 if show_smart:
210 p.add_option('-s', '--smart-sync',
211 dest='smart_sync', action='store_true',
212 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200213 p.add_option('-t', '--smart-tag',
214 dest='smart_tag', action='store',
215 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700216
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700217 g = p.add_option_group('repo Version options')
218 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700219 dest='no_repo_verify', action='store_true',
220 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700221 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800222 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700223 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700224
David James89ece422014-01-09 18:51:58 -0800225 def _FetchProjectList(self, opt, projects, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900226 """Main function of the fetch threads when jobs are > 1.
Doug Andersonfc06ced2011-03-16 15:49:18 -0700227
David James8d201162013-10-11 17:03:19 -0700228 Delegates most of the work to _FetchHelper.
229
230 Args:
231 opt: Program options returned from optparse. See _Options().
232 projects: Projects to fetch.
David James89ece422014-01-09 18:51:58 -0800233 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700234 _FetchHelper docstring for details.
235 """
236 for project in projects:
David James89ece422014-01-09 18:51:58 -0800237 success = self._FetchHelper(opt, project, *args, **kwargs)
David James8d201162013-10-11 17:03:19 -0700238 if not success and not opt.force_broken:
239 break
240
241 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
242 """Fetch git objects for a single project.
243
David Pursehousec1b86a22012-11-14 11:36:51 +0900244 Args:
245 opt: Program options returned from optparse. See _Options().
246 project: Project object for the project to fetch.
247 lock: Lock for accessing objects that are shared amongst multiple
248 _FetchHelper() threads.
249 fetched: set object that we will add project.gitdir to when we're done
250 (with our lock held).
251 pm: Instance of a Project object. We will call pm.update() (with our
252 lock held).
253 sem: We'll release() this semaphore when we exit so that another thread
254 can be started up.
255 err_event: We'll set this event in the case of an error (after printing
256 out info about the error).
David James8d201162013-10-11 17:03:19 -0700257
258 Returns:
259 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900260 """
261 # We'll set to true once we've locked the lock.
262 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700263
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530264 if not opt.quiet:
265 print('Fetching project %s' % project.name)
266
David Pursehousec1b86a22012-11-14 11:36:51 +0900267 # Encapsulate everything in a try/except/finally so that:
268 # - We always set err_event in the case of an exception.
269 # - We always make sure we call sem.release().
270 # - We always make sure we unlock the lock if we locked it.
271 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700272 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900273 start = time.time()
274 success = project.Sync_NetworkHalf(
275 quiet=opt.quiet,
276 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700277 clone_bundle=not opt.no_clone_bundle,
Julien Campergue335f5ef2013-10-16 11:02:35 +0200278 no_tags=opt.no_tags, archive=self.manifest.IsArchive)
David Pursehousec1b86a22012-11-14 11:36:51 +0900279 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700280
David Pursehousec1b86a22012-11-14 11:36:51 +0900281 # Lock around all the rest of the code, since printing, updating a set
282 # and Progress.update() are not thread safe.
283 lock.acquire()
284 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700285
David Pursehousec1b86a22012-11-14 11:36:51 +0900286 if not success:
287 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
288 if opt.force_broken:
289 print('warn: --force-broken, continuing to sync',
290 file=sys.stderr)
291 else:
292 raise _FetchError()
Roy Lee18afd7f2010-05-09 04:32:08 +0800293
David Pursehousec1b86a22012-11-14 11:36:51 +0900294 fetched.add(project.gitdir)
295 pm.update()
296 except _FetchError:
297 err_event.set()
298 except:
299 err_event.set()
300 raise
301 finally:
302 if did_lock:
303 lock.release()
304 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800305
David James8d201162013-10-11 17:03:19 -0700306 return success
307
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700308 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700309 fetched = set()
David James89ece422014-01-09 18:51:58 -0800310 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700311 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800312
David James89ece422014-01-09 18:51:58 -0800313 objdir_project_map = dict()
314 for project in projects:
315 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700316
David James89ece422014-01-09 18:51:58 -0800317 threads = set()
318 sem = _threading.Semaphore(self.jobs)
319 err_event = _threading.Event()
320 for project_list in objdir_project_map.values():
321 # Check for any errors before running any more tasks.
322 # ...we'll let existing threads finish, though.
323 if err_event.isSet() and not opt.force_broken:
324 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700325
David James89ece422014-01-09 18:51:58 -0800326 sem.acquire()
327 kwargs = dict(opt=opt,
328 projects=project_list,
329 lock=lock,
330 fetched=fetched,
331 pm=pm,
332 sem=sem,
333 err_event=err_event)
334 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700335 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800336 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200337 # Ensure that Ctrl-C will not freeze the repo process.
338 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800339 threads.add(t)
340 t.start()
David James89ece422014-01-09 18:51:58 -0800341 else:
342 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800343
David James89ece422014-01-09 18:51:58 -0800344 for t in threads:
345 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800346
David James89ece422014-01-09 18:51:58 -0800347 # If we saw an error, exit with code 1 so that other scripts can check.
348 if err_event.isSet():
349 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
350 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700351
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700352 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700353 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700354
Julien Campergue335f5ef2013-10-16 11:02:35 +0200355 if not self.manifest.IsArchive:
356 self._GCProjects(projects)
357
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700358 return fetched
359
Dave Borowitz18857212012-10-23 17:02:59 -0700360 def _GCProjects(self, projects):
David James8d201162013-10-11 17:03:19 -0700361 gitdirs = {}
362 for project in projects:
363 gitdirs[project.gitdir] = project.bare_git
364
Dave Borowitze2152672012-10-31 12:24:38 -0700365 has_dash_c = git_require((1, 7, 2))
366 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700367 cpu_count = multiprocessing.cpu_count()
368 else:
369 cpu_count = 1
370 jobs = min(self.jobs, cpu_count)
371
372 if jobs < 2:
David James8d201162013-10-11 17:03:19 -0700373 for bare_git in gitdirs.values():
374 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700375 return
376
377 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
378
379 threads = set()
380 sem = _threading.Semaphore(jobs)
381 err_event = _threading.Event()
382
David James8d201162013-10-11 17:03:19 -0700383 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700384 try:
385 try:
David James8d201162013-10-11 17:03:19 -0700386 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700387 except GitError:
388 err_event.set()
389 except:
390 err_event.set()
391 raise
392 finally:
393 sem.release()
394
David James8d201162013-10-11 17:03:19 -0700395 for bare_git in gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700396 if err_event.isSet():
397 break
398 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700399 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700400 t.daemon = True
401 threads.add(t)
402 t.start()
403
404 for t in threads:
405 t.join()
406
407 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700408 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700409 sys.exit(1)
410
Tim Kilbourn07669002013-03-08 15:02:49 -0800411 def _ReloadManifest(self, manifest_name=None):
412 if manifest_name:
413 # Override calls _Unload already
414 self.manifest.Override(manifest_name)
415 else:
416 self.manifest._Unload()
417
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700418 def UpdateProjectList(self):
419 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700420 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700421 if project.relpath:
422 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700423 file_name = 'project.list'
424 file_path = os.path.join(self.manifest.repodir, file_name)
425 old_project_paths = []
426
427 if os.path.exists(file_path):
428 fd = open(file_path, 'r')
429 try:
430 old_project_paths = fd.read().split('\n')
431 finally:
432 fd.close()
433 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700434 if not path:
435 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700436 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900437 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400438 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700439 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900440 project = Project(
441 manifest = self.manifest,
442 name = path,
443 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700444 gitdir = gitdir,
445 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900446 worktree = os.path.join(self.manifest.topdir, path),
447 relpath = path,
448 revisionExpr = 'HEAD',
449 revisionId = None,
450 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400451
David Pursehousec1b86a22012-11-14 11:36:51 +0900452 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900453 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900454 'are present' % project.relpath, file=sys.stderr)
455 print(' commit changes, then run sync again',
456 file=sys.stderr)
457 return -1
458 else:
459 print('Deleting obsolete path %s' % project.worktree,
460 file=sys.stderr)
461 shutil.rmtree(project.worktree)
462 # Try deleting parent subdirs if they are empty
463 project_dir = os.path.dirname(project.worktree)
464 while project_dir != self.manifest.topdir:
465 try:
466 os.rmdir(project_dir)
467 except OSError:
468 break
469 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700470
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700471 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700472 fd = open(file_path, 'w')
473 try:
474 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700475 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700476 finally:
477 fd.close()
478 return 0
479
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700480 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800481 if opt.jobs:
482 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700483 if self.jobs > 1:
484 soft_limit, _ = _rlimit_nofile()
485 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
486
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700487 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700488 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700489 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700490 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700491 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700492 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500493 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700494 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500495 sys.exit(1)
496 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700497 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500498 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900499 if opt.manifest_server_username or opt.manifest_server_password:
500 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700501 print('error: -u and -p may only be combined with -s or -t',
502 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900503 sys.exit(1)
504 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700505 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900506 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500507
508 if opt.manifest_name:
509 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700510
Chirayu Desaia892b102013-06-11 14:18:46 +0530511 manifest_name = opt.manifest_name
512
Victor Boivie08c880d2011-04-19 10:32:52 +0200513 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700514 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900515 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700516 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700517 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900518
519 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900520 if not opt.quiet:
521 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900522
David Pursehouse86d973d2012-08-24 10:21:02 +0900523 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900524 username = None
525 password = None
526 if opt.manifest_server_username and opt.manifest_server_password:
527 username = opt.manifest_server_username
528 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900529 else:
530 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900531 info = netrc.netrc()
532 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700533 print('.netrc file does not exist or could not be opened',
534 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900535 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900536 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530537 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900538 if parse_result.hostname:
539 username, _account, password = \
540 info.authenticators(parse_result.hostname)
541 except TypeError:
542 # TypeError is raised when the given hostname is not present
543 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700544 print('No credentials found for %s in .netrc'
545 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700546 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700547 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900548
549 if (username and password):
550 manifest_server = manifest_server.replace('://', '://%s:%s@' %
551 (username, password),
552 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900553
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700554 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530555 server = xmlrpc.client.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200556 if opt.smart_sync:
557 p = self.manifest.manifestProject
558 b = p.GetBranch(p.CurrentBranch)
559 branch = b.merge
560 if branch.startswith(R_HEADS):
561 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700562
Victor Boivie08c880d2011-04-19 10:32:52 +0200563 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700564 if 'SYNC_TARGET' in env:
565 target = env['SYNC_TARGET']
566 [success, manifest_str] = server.GetApprovedManifest(branch, target)
567 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200568 target = '%s-%s' % (env['TARGET_PRODUCT'],
569 env['TARGET_BUILD_VARIANT'])
570 [success, manifest_str] = server.GetApprovedManifest(branch, target)
571 else:
572 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700573 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200574 assert(opt.smart_tag)
575 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700576
577 if success:
578 manifest_name = "smart_sync_override.xml"
579 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
580 manifest_name)
581 try:
582 f = open(manifest_path, 'w')
583 try:
584 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700585 finally:
586 f.close()
587 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700588 print('error: cannot write manifest to %s' % manifest_path,
589 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700590 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100591 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700592 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900593 print('error: manifest server RPC call failed: %s' %
594 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700595 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530596 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700597 print('error: cannot connect to manifest server %s:\n%s'
598 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900599 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530600 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700601 print('error: cannot connect to manifest server %s:\n%d %s'
602 % (self.manifest.manifest_server, e.errcode, e.errmsg),
603 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700604 sys.exit(1)
605
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700606 rp = self.manifest.repoProject
607 rp.PreSync()
608
609 mp = self.manifest.manifestProject
610 mp.PreSync()
611
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800612 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700613 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800614
Nico Sallembien9bb18162009-12-07 15:38:01 -0800615 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700616 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700617 current_branch_only=opt.current_branch_only,
618 no_tags=opt.no_tags)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800619
620 if mp.HasChanges:
621 syncbuf = SyncBuffer(mp.config)
622 mp.Sync_LocalHalf(syncbuf)
623 if not syncbuf.Finish():
624 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100625 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700626 if opt.jobs is None:
627 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800628 all_projects = self.GetProjects(args,
629 missing_ok=True,
630 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700631
Dave Borowitz67700e92012-10-23 15:00:54 -0700632 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700633 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700634 to_fetch = []
635 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700636 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700637 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900638 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700639 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700640
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800641 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700642 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700643 if opt.network_only:
644 # bail out now; the rest touches the working tree
645 return
646
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800647 # Iteratively fetch missing and/or nested unregistered submodules
648 previously_missing_set = set()
649 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100650 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800651 all_projects = self.GetProjects(args,
652 missing_ok=True,
653 submodules_ok=opt.fetch_submodules)
654 missing = []
655 for project in all_projects:
656 if project.gitdir not in fetched:
657 missing.append(project)
658 if not missing:
659 break
660 # Stop us from non-stopped fetching actually-missing repos: If set of
661 # missing repos has not been changed from last fetch, we break.
662 missing_set = set(p.name for p in missing)
663 if previously_missing_set == missing_set:
664 break
665 previously_missing_set = missing_set
666 fetched.update(self._Fetch(missing, opt))
667
Julien Campergue335f5ef2013-10-16 11:02:35 +0200668 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700669 # bail out now, we have no working tree
670 return
671
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700672 if self.UpdateProjectList():
673 sys.exit(1)
674
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700675 syncbuf = SyncBuffer(mp.config,
676 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900677 pm = Progress('Syncing work tree', len(all_projects))
678 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700679 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800680 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700681 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700682 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700683 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700684 if not syncbuf.Finish():
685 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700686
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700687 # If there's a notice that's supposed to print at the end of the sync, print
688 # it now...
689 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700690 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700691
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700692def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800693 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700694 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700695 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800696 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700697 if project.Exists:
698 project.PostRepoUpgrade()
699
700def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
701 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700702 print('info: A new version of repo is available', file=sys.stderr)
703 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700704 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700705 syncbuf = SyncBuffer(rp.config)
706 rp.Sync_LocalHalf(syncbuf)
707 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700708 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700709 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700710 raise RepoChangedException(['--repo-upgraded'])
711 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700712 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700713 else:
714 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700715 print('repo version %s is current' % rp.work_git.describe(HEAD),
716 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700717
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700718def _VerifyTag(project):
719 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
720 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700721 print('warning: GnuPG was not available during last "repo init"\n'
722 'warning: Cannot automatically authenticate repo."""',
723 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700724 return True
725
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700726 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700727 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700728 except GitError:
729 cur = None
730
731 if not cur \
732 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700733 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700734 if rev.startswith(R_HEADS):
735 rev = rev[len(R_HEADS):]
736
Sarah Owenscecd1d82012-11-01 22:59:27 -0700737 print(file=sys.stderr)
738 print("warning: project '%s' branch '%s' is not signed"
739 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700740 return False
741
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800742 env = os.environ.copy()
743 env['GIT_DIR'] = project.gitdir.encode()
744 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745
746 cmd = [GIT, 'tag', '-v', cur]
747 proc = subprocess.Popen(cmd,
748 stdout = subprocess.PIPE,
749 stderr = subprocess.PIPE,
750 env = env)
751 out = proc.stdout.read()
752 proc.stdout.close()
753
754 err = proc.stderr.read()
755 proc.stderr.close()
756
757 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700758 print(file=sys.stderr)
759 print(out, file=sys.stderr)
760 print(err, file=sys.stderr)
761 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700762 return False
763 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700764
765class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700766 _ALPHA = 0.5
767
Dave Borowitz67700e92012-10-23 15:00:54 -0700768 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100769 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700770 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700771 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700772
773 def Get(self, project):
774 self._Load()
775 return self._times.get(project.name, _ONE_DAY_S)
776
777 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700778 self._Load()
779 name = project.name
780 old = self._times.get(name, t)
781 self._seen.add(name)
782 a = self._ALPHA
783 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700784
785 def _Load(self):
786 if self._times is None:
787 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100788 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700789 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100790 self._times = json.load(f)
791 finally:
792 f.close()
793 except (IOError, ValueError):
794 try:
795 os.remove(self._path)
796 except OSError:
797 pass
798 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700799
800 def Save(self):
801 if self._times is None:
802 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700803
804 to_delete = []
805 for name in self._times:
806 if name not in self._seen:
807 to_delete.append(name)
808 for name in to_delete:
809 del self._times[name]
810
Dave Borowitz67700e92012-10-23 15:00:54 -0700811 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100812 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700813 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100814 json.dump(self._times, f, indent=2)
815 finally:
816 f.close()
817 except (IOError, TypeError):
818 try:
819 os.remove(self._path)
820 except OSError:
821 pass