[Tizen] Add prelauncher
[platform/framework/web/crosswalk-tizen.git] / vendor / depot_tools / fetch.py
1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """
7 Tool to perform checkouts in one easy command line!
8
9 Usage:
10   fetch <recipe> [--property=value [--property2=value2 ...]]
11
12 This script is a wrapper around various version control and repository
13 checkout commands. It requires a |recipe| name, fetches data from that
14 recipe in depot_tools/recipes, and then performs all necessary inits,
15 checkouts, pulls, fetches, etc.
16
17 Optional arguments may be passed on the command line in key-value pairs.
18 These parameters will be passed through to the recipe's main method.
19 """
20
21 import json
22 import optparse
23 import os
24 import pipes
25 import subprocess
26 import sys
27 import textwrap
28
29 from distutils import spawn
30
31
32 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
33
34 #################################################
35 # Checkout class definitions.
36 #################################################
37 class Checkout(object):
38   """Base class for implementing different types of checkouts.
39
40   Attributes:
41     |base|: the absolute path of the directory in which this script is run.
42     |spec|: the spec for this checkout as returned by the recipe. Different
43         subclasses will expect different keys in this dictionary.
44     |root|: the directory into which the checkout will be performed, as returned
45         by the recipe. This is a relative path from |base|.
46   """
47   def __init__(self, options, spec, root):
48     self.base = os.getcwd()
49     self.options = options
50     self.spec = spec
51     self.root = root
52
53   def exists(self):
54     pass
55
56   def init(self):
57     pass
58
59   def sync(self):
60     pass
61
62   def run(self, cmd, **kwargs):
63     print 'Running: %s' % (' '.join(pipes.quote(x) for x in cmd))
64     if self.options.dry_run:
65       return 0
66     return subprocess.check_call(cmd, **kwargs)
67
68
69 class GclientCheckout(Checkout):
70
71   def run_gclient(self, *cmd, **kwargs):
72     if not spawn.find_executable('gclient'):
73       cmd_prefix = (sys.executable, os.path.join(SCRIPT_PATH, 'gclient.py'))
74     else:
75       cmd_prefix = ('gclient',)
76     return self.run(cmd_prefix + cmd, **kwargs)
77
78
79 class GitCheckout(Checkout):
80
81   def run_git(self, *cmd, **kwargs):
82     if sys.platform == 'win32' and not spawn.find_executable('git'):
83       git_path = os.path.join(SCRIPT_PATH, 'git.bat')
84     else:
85       git_path = 'git'
86     return self.run((git_path,) + cmd, **kwargs)
87
88
89 class SvnCheckout(Checkout):
90
91   def run_svn(self, *cmd, **kwargs):
92     if sys.platform == 'win32' and not spawn.find_executable('svn'):
93       svn_path = os.path.join(SCRIPT_PATH, 'svn_bin', 'svn.exe')
94     else:
95       svn_path = 'svn'
96     return self.run((svn_path,) + cmd, **kwargs)
97
98
99 class GclientGitCheckout(GclientCheckout, GitCheckout):
100
101   def __init__(self, options, spec, root):
102     super(GclientGitCheckout, self).__init__(options, spec, root)
103     assert 'solutions' in self.spec
104
105   def _format_spec(self):
106     def _format_literal(lit):
107       if isinstance(lit, basestring):
108         return '"%s"' % lit
109       if isinstance(lit, list):
110         return '[%s]' % ', '.join(_format_literal(i) for i in lit)
111       return '%r' % lit
112     soln_strings = []
113     for soln in self.spec['solutions']:
114       soln_string= '\n'.join('    "%s": %s,' % (key, _format_literal(value))
115                              for key, value in soln.iteritems())
116       soln_strings.append('  {\n%s\n  },' % soln_string)
117     gclient_spec = 'solutions = [\n%s\n]\n' % '\n'.join(soln_strings)
118     extra_keys = ['target_os', 'target_os_only']
119     gclient_spec += ''.join('%s = %s\n' % (key, _format_literal(self.spec[key]))
120                              for key in extra_keys if key in self.spec)
121     return gclient_spec
122
123   def exists(self):
124     return os.path.exists(os.path.join(os.getcwd(), self.root))
125
126   def init(self):
127     # Configure and do the gclient checkout.
128     self.run_gclient('config', '--spec', self._format_spec())
129     sync_cmd = ['sync']
130     if self.options.nohooks:
131       sync_cmd.append('--nohooks')
132     if self.options.no_history:
133       sync_cmd.append('--no-history')
134     if self.spec.get('with_branch_heads', False):
135       sync_cmd.append('--with_branch_heads')
136     self.run_gclient(*sync_cmd)
137
138     # Configure git.
139     wd = os.path.join(self.base, self.root)
140     if self.options.dry_run:
141       print 'cd %s' % wd
142     self.run_git(
143         'submodule', 'foreach',
144         'git config -f $toplevel/.git/config submodule.$name.ignore all',
145         cwd=wd)
146     self.run_git(
147         'config', '--add', 'remote.origin.fetch',
148         '+refs/tags/*:refs/tags/*', cwd=wd)
149     self.run_git('config', 'diff.ignoreSubmodules', 'all', cwd=wd)
150
151
152 class GclientGitSvnCheckout(GclientGitCheckout, SvnCheckout):
153
154   def __init__(self, options, spec, root):
155     super(GclientGitSvnCheckout, self).__init__(options, spec, root)
156
157   def init(self):
158     # Ensure we are authenticated with subversion for all submodules.
159     git_svn_dirs = json.loads(self.spec.get('submodule_git_svn_spec', '{}'))
160     git_svn_dirs.update({self.root: self.spec})
161     for _, svn_spec in git_svn_dirs.iteritems():
162       if svn_spec.get('svn_url'):
163         try:
164           self.run_svn('ls', '--non-interactive', svn_spec['svn_url'])
165         except subprocess.CalledProcessError:
166           print 'Please run `svn ls %s`' % svn_spec['svn_url']
167           return 1
168
169     super(GclientGitSvnCheckout, self).init()
170
171     # Configure git-svn.
172     for path, svn_spec in git_svn_dirs.iteritems():
173       real_path = os.path.join(*path.split('/'))
174       if real_path != self.root:
175         real_path = os.path.join(self.root, real_path)
176       wd = os.path.join(self.base, real_path)
177       if self.options.dry_run:
178         print 'cd %s' % wd
179       if svn_spec.get('auto'):
180         self.run_git('auto-svn', cwd=wd)
181         continue
182       self.run_git('svn', 'init', svn_spec['svn_url'], cwd=wd)
183       self.run_git('config', '--unset-all', 'svn-remote.svn.fetch', cwd=wd)
184       for svn_branch, git_ref in svn_spec.get('git_svn_fetch', {}).items():
185         self.run_git('config', '--add', 'svn-remote.svn.fetch',
186                      '%s:%s' % (svn_branch, git_ref), cwd=wd)
187       for svn_branch, git_ref in svn_spec.get('git_svn_branches', {}).items():
188         self.run_git('config', '--add', 'svn-remote.svn.branches',
189                      '%s:%s' % (svn_branch, git_ref), cwd=wd)
190       self.run_git('svn', 'fetch', cwd=wd)
191
192
193
194 CHECKOUT_TYPE_MAP = {
195     'gclient':         GclientCheckout,
196     'gclient_git':     GclientGitCheckout,
197     'gclient_git_svn': GclientGitSvnCheckout,
198     'git':             GitCheckout,
199 }
200
201
202 def CheckoutFactory(type_name, options, spec, root):
203   """Factory to build Checkout class instances."""
204   class_ = CHECKOUT_TYPE_MAP.get(type_name)
205   if not class_:
206     raise KeyError('unrecognized checkout type: %s' % type_name)
207   return class_(options, spec, root)
208
209
210 #################################################
211 # Utility function and file entry point.
212 #################################################
213 def usage(msg=None):
214   """Print help and exit."""
215   if msg:
216     print 'Error:', msg
217
218   print textwrap.dedent("""\
219     usage: %s [options] <recipe> [--property=value [--property2=value2 ...]]
220
221     This script can be used to download the Chromium sources. See
222     http://www.chromium.org/developers/how-tos/get-the-code
223     for full usage instructions.
224
225     Valid options:
226        -h, --help, help   Print this message.
227        --nohooks          Don't run hooks after checkout.
228        -n, --dry-run      Don't run commands, only print them.
229        --no-history       Perform shallow clones, don't fetch the full git history.
230
231     Valid fetch recipes:""") % os.path.basename(sys.argv[0])
232   for fname in os.listdir(os.path.join(SCRIPT_PATH, 'recipes')):
233     if fname.endswith('.py'):
234       print '  ' + fname[:-3]
235
236   sys.exit(bool(msg))
237
238
239 def handle_args(argv):
240   """Gets the recipe name from the command line arguments."""
241   if len(argv) <= 1:
242     usage('Must specify a recipe.')
243   if argv[1] in ('-h', '--help', 'help'):
244     usage()
245
246   dry_run = False
247   nohooks = False
248   no_history = False
249   while len(argv) >= 2:
250     arg = argv[1]
251     if not arg.startswith('-'):
252       break
253     argv.pop(1)
254     if arg in ('-n', '--dry-run'):
255       dry_run = True
256     elif arg == '--nohooks':
257       nohooks = True
258     elif arg == '--no-history':
259       no_history = True
260     else:
261       usage('Invalid option %s.' % arg)
262
263   def looks_like_arg(arg):
264     return arg.startswith('--') and arg.count('=') == 1
265
266   bad_parms = [x for x in argv[2:] if not looks_like_arg(x)]
267   if bad_parms:
268     usage('Got bad arguments %s' % bad_parms)
269
270   recipe = argv[1]
271   props = argv[2:]
272   return (
273       optparse.Values(
274           {'dry_run':dry_run, 'nohooks':nohooks, 'no_history': no_history }),
275       recipe,
276       props)
277
278
279 def run_recipe_fetch(recipe, props, aliased=False):
280   """Invoke a recipe's fetch method with the passed-through args
281   and return its json output as a python object."""
282   recipe_path = os.path.abspath(os.path.join(SCRIPT_PATH, 'recipes', recipe))
283   if not os.path.exists(recipe_path + '.py'):
284     print "Could not find a recipe for %s" % recipe
285     sys.exit(1)
286
287   cmd = [sys.executable, recipe_path + '.py', 'fetch'] + props
288   result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
289
290   spec = json.loads(result)
291   if 'alias' in spec:
292     assert not aliased
293     return run_recipe_fetch(
294         spec['alias']['recipe'], spec['alias']['props'] + props, aliased=True)
295   cmd = [sys.executable, recipe_path + '.py', 'root']
296   result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
297   root = json.loads(result)
298   return spec, root
299
300
301 def run(options, spec, root):
302   """Perform a checkout with the given type and configuration.
303
304     Args:
305       options: Options instance.
306       spec: Checkout configuration returned by the the recipe's fetch_spec
307           method (checkout type, repository url, etc.).
308       root: The directory into which the repo expects to be checkout out.
309   """
310   assert 'type' in spec
311   checkout_type = spec['type']
312   checkout_spec = spec['%s_spec' % checkout_type]
313   try:
314     checkout = CheckoutFactory(checkout_type, options, checkout_spec, root)
315   except KeyError:
316     return 1
317   if checkout.exists():
318     print 'You appear to already have a checkout. "fetch" is used only'
319     print 'to get new checkouts. Use "gclient sync" to update the checkout.'
320     print
321     print 'Fetch also does not yet deal with partial checkouts, so if fetch'
322     print 'failed, delete the checkout and start over (crbug.com/230691).'
323     return 1
324   return checkout.init()
325
326
327 def main():
328   options, recipe, props = handle_args(sys.argv)
329   spec, root = run_recipe_fetch(recipe, props)
330   return run(options, spec, root)
331
332
333 if __name__ == '__main__':
334   sys.exit(main())