Revert "Merged all Chromoting Host code into remoting_core.dll (Windows)."
[chromium-blink-merge.git] / native_client_sdk / src / build_tools / sdk_tools / sdk_update_main.py
blob3dc56e58426c438f4a0727e4f9d3a58ebc0b9837
1 #!/usr/bin/env python
2 # Copyright (c) 2012 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.
6 # CMD code copied from git_cl.py in depot_tools.
8 import config
9 import cStringIO
10 import download
11 import json
12 import logging
13 import optparse
14 import os
15 import re
16 import sdk_update_common
17 from sdk_update_common import Error
18 import sys
19 import urllib2
21 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
22 PARENT_DIR = os.path.dirname(SCRIPT_DIR)
24 sys.path.append(os.path.dirname(SCRIPT_DIR))
25 import manifest_util
28 # Import late so each command script can find our imports
29 import command.info
30 import command.list
31 import command.sources
32 import command.update
34 # This revision number is autogenerated from the Chrome revision.
35 REVISION = '{REVISION}'
37 GSTORE_URL = 'https://commondatastorage.googleapis.com/nativeclient-mirror'
38 CONFIG_FILENAME = 'naclsdk_config.json'
39 MANIFEST_FILENAME = 'naclsdk_manifest2.json'
40 DEFAULT_SDK_ROOT = os.path.abspath(PARENT_DIR)
41 USER_DATA_DIR = os.path.join(DEFAULT_SDK_ROOT, 'sdk_cache')
44 def usage(more):
45 def hook(fn):
46 fn.usage_more = more
47 return fn
48 return hook
51 def hide(fn):
52 fn.hide = True
53 return fn
56 def LoadConfig(raise_on_error=False):
57 path = os.path.join(USER_DATA_DIR, CONFIG_FILENAME)
58 if not os.path.exists(path):
59 return config.Config()
61 try:
62 try:
63 with open(path) as f:
64 return config.Config(json.loads(f.read()))
65 except IOError as e:
66 raise Error('Unable to read config from "%s".\n %s' % (path, e))
67 except Exception as e:
68 raise Error('Parsing config file from "%s" failed.\n %s' % (path, e))
69 except Error as e:
70 if raise_on_error:
71 raise
72 else:
73 logging.warn(str(e))
74 return config.Config()
77 def WriteConfig(cfg):
78 path = os.path.join(USER_DATA_DIR, CONFIG_FILENAME)
79 try:
80 sdk_update_common.MakeDirs(USER_DATA_DIR)
81 except Exception as e:
82 raise Error('Unable to create directory "%s".\n %s' % (USER_DATA_DIR, e))
84 try:
85 cfg_json = cfg.ToJson()
86 except Exception as e:
87 raise Error('Json encoding error writing config "%s".\n %s' % (path, e))
89 try:
90 with open(path, 'w') as f:
91 f.write(cfg_json)
92 except IOError as e:
93 raise Error('Unable to write config to "%s".\n %s' % (path, e))
96 def LoadLocalManifest(raise_on_error=False):
97 path = os.path.join(USER_DATA_DIR, MANIFEST_FILENAME)
98 manifest = manifest_util.SDKManifest()
99 try:
100 try:
101 with open(path) as f:
102 manifest_string = f.read()
103 except IOError as e:
104 raise Error('Unable to read manifest from "%s".\n %s' % (path, e))
106 try:
107 manifest.LoadDataFromString(manifest_string)
108 except Exception as e:
109 raise Error('Parsing local manifest "%s" failed.\n %s' % (path, e))
110 except Error as e:
111 if raise_on_error:
112 raise
113 else:
114 logging.warn(str(e))
115 return manifest
118 def WriteLocalManifest(manifest):
119 path = os.path.join(USER_DATA_DIR, MANIFEST_FILENAME)
120 try:
121 sdk_update_common.MakeDirs(USER_DATA_DIR)
122 except Exception as e:
123 raise Error('Unable to create directory "%s".\n %s' % (USER_DATA_DIR, e))
125 try:
126 manifest_json = manifest.GetDataAsString()
127 except Exception as e:
128 raise Error('Error encoding manifest "%s" to JSON.\n %s' % (path, e))
130 try:
131 with open(path, 'w') as f:
132 f.write(manifest_json)
133 except IOError as e:
134 raise Error('Unable to write manifest to "%s".\n %s' % (path, e))
137 def LoadRemoteManifest(url):
138 manifest = manifest_util.SDKManifest()
139 url_stream = None
140 try:
141 manifest_stream = cStringIO.StringIO()
142 url_stream = download.UrlOpen(url)
143 download.DownloadAndComputeHash(url_stream, manifest_stream)
144 except urllib2.URLError as e:
145 raise Error('Unable to read remote manifest from URL "%s".\n %s' % (
146 url, e))
147 finally:
148 if url_stream:
149 url_stream.close()
151 try:
152 manifest.LoadDataFromString(manifest_stream.getvalue())
153 return manifest
154 except manifest_util.Error as e:
155 raise Error('Parsing remote manifest from URL "%s" failed.\n %s' % (
156 url, e,))
159 def LoadCombinedRemoteManifest(default_manifest_url, cfg):
160 manifest = LoadRemoteManifest(default_manifest_url)
161 for source in cfg.sources:
162 manifest.MergeManifest(LoadRemoteManifest(source))
163 return manifest
166 # Commands #####################################################################
169 @usage('<bundle names...>')
170 def CMDinfo(parser, args):
171 """display information about a bundle"""
172 options, args = parser.parse_args(args)
173 if len(args) == 0:
174 parser.error('No bundles given')
175 return 0
176 cfg = LoadConfig()
177 remote_manifest = LoadCombinedRemoteManifest(options.manifest_url, cfg)
178 command.info.Info(remote_manifest, args)
179 return 0
182 def CMDlist(parser, args):
183 """list all available bundles"""
184 parser.add_option('-r', '--revision', action='store_true',
185 help='display revision numbers')
186 options, args = parser.parse_args(args)
187 if args:
188 parser.error('Unsupported argument(s): %s' % ', '.join(args))
189 local_manifest = LoadLocalManifest()
190 cfg = LoadConfig()
191 remote_manifest = LoadCombinedRemoteManifest(options.manifest_url, cfg)
192 command.list.List(remote_manifest, local_manifest, options.revision)
193 return 0
196 @usage('<bundle names...>')
197 def CMDupdate(parser, args):
198 """update a bundle in the SDK to the latest version"""
199 parser.add_option(
200 '-F', '--force', action='store_true',
201 help='Force updating existing components that already exist')
202 options, args = parser.parse_args(args)
203 local_manifest = LoadLocalManifest()
204 cfg = LoadConfig()
205 remote_manifest = LoadCombinedRemoteManifest(options.manifest_url, cfg)
207 if not args:
208 args = [command.update.RECOMMENDED]
210 try:
211 delegate = command.update.RealUpdateDelegate(USER_DATA_DIR,
212 DEFAULT_SDK_ROOT)
213 command.update.Update(delegate, remote_manifest, local_manifest, args,
214 options.force)
215 finally:
216 # Always write out the local manifest, we may have successfully updated one
217 # or more bundles before failing.
218 try:
219 WriteLocalManifest(local_manifest)
220 except Error as e:
221 # Log the error writing to the manifest, but propagate the original
222 # exception.
223 logging.error(str(e))
225 return 0
228 def CMDinstall(parser, args):
229 """install a bundle in the SDK"""
230 # For now, forward to CMDupdate. We may want different behavior for this
231 # in the future, though...
232 return CMDupdate(parser, args)
235 def CMDsources(parser, args):
236 """manage external package sources"""
237 parser.add_option('-a', '--add', dest='url_to_add',
238 help='Add an additional package source')
239 parser.add_option(
240 '-r', '--remove', dest='url_to_remove',
241 help='Remove package source (use \'all\' for all additional sources)')
242 parser.add_option('-l', '--list', dest='do_list', action='store_true',
243 help='List additional package sources')
244 options, args = parser.parse_args(args)
246 cfg = LoadConfig(True)
247 write_config = False
248 if options.url_to_add:
249 command.sources.AddSource(cfg, options.url_to_add)
250 write_config = True
251 elif options.url_to_remove:
252 command.sources.RemoveSource(cfg, options.url_to_remove)
253 write_config = True
254 elif options.do_list:
255 command.sources.ListSources(cfg)
256 else:
257 parser.print_help()
259 if write_config:
260 WriteConfig(cfg)
262 return 0
265 def CMDversion(parser, args):
266 """display version information"""
267 _, _ = parser.parse_args(args)
268 print "Native Client SDK Updater, version r%s" % REVISION
269 return 0
272 def CMDhelp(parser, args):
273 """print list of commands or help for a specific command"""
274 _, args = parser.parse_args(args)
275 if len(args) == 1:
276 return main(args + ['--help'])
277 parser.print_help()
278 return 0
281 def Command(name):
282 return globals().get('CMD' + name, None)
285 def GenUsage(parser, cmd):
286 """Modify an OptParse object with the function's documentation."""
287 obj = Command(cmd)
288 more = getattr(obj, 'usage_more', '')
289 if cmd == 'help':
290 cmd = '<command>'
291 else:
292 # OptParser.description prefer nicely non-formatted strings.
293 parser.description = re.sub('[\r\n ]{2,}', ' ', obj.__doc__)
294 parser.set_usage('usage: %%prog %s [options] %s' % (cmd, more))
297 def UpdateSDKTools(options, args):
298 """update the sdk_tools bundle"""
300 local_manifest = LoadLocalManifest()
301 cfg = LoadConfig()
302 remote_manifest = LoadCombinedRemoteManifest(options.manifest_url, cfg)
304 try:
305 delegate = command.update.RealUpdateDelegate(USER_DATA_DIR,
306 DEFAULT_SDK_ROOT)
307 command.update.UpdateBundleIfNeeded(
308 delegate,
309 remote_manifest,
310 local_manifest,
311 command.update.SDK_TOOLS,
312 force=True)
313 finally:
314 # Always write out the local manifest, we may have successfully updated one
315 # or more bundles before failing.
316 WriteLocalManifest(local_manifest)
317 return 0
320 def main(argv):
321 # Get all commands...
322 cmds = [fn[3:] for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')]
323 # Remove hidden commands...
324 cmds = filter(lambda fn: not getattr(Command(fn), 'hide', 0), cmds)
325 # Format for CMDhelp usage.
326 CMDhelp.usage_more = ('\n\nCommands are:\n' + '\n'.join([
327 ' %-10s %s' % (fn, Command(fn).__doc__.split('\n')[0].strip())
328 for fn in cmds]))
330 # Create the option parse and add --verbose support.
331 parser = optparse.OptionParser()
332 parser.add_option(
333 '-v', '--verbose', action='count', default=0,
334 help='Use 2 times for more debugging info')
335 parser.add_option('-U', '--manifest-url', dest='manifest_url',
336 default=GSTORE_URL + '/nacl/nacl_sdk/' + MANIFEST_FILENAME,
337 metavar='URL', help='override the default URL for the NaCl manifest file')
338 parser.add_option('--update-sdk-tools', action='store_true',
339 dest='update_sdk_tools', help=optparse.SUPPRESS_HELP)
341 old_parser_args = parser.parse_args
342 def Parse(args):
343 options, args = old_parser_args(args)
344 if options.verbose >= 2:
345 loglevel = logging.DEBUG
346 elif options.verbose:
347 loglevel = logging.INFO
348 else:
349 loglevel = logging.WARNING
351 fmt = '%(levelname)s:%(message)s'
352 logging.basicConfig(stream=sys.stdout, level=loglevel, format=fmt)
354 # If --update-sdk-tools is passed, circumvent any other command running.
355 if options.update_sdk_tools:
356 UpdateSDKTools(options, args)
357 sys.exit(1)
359 return options, args
360 parser.parse_args = Parse
362 if argv:
363 cmd = Command(argv[0])
364 if cmd:
365 # "fix" the usage and the description now that we know the subcommand.
366 GenUsage(parser, argv[0])
367 return cmd(parser, argv[1:])
369 # Not a known command. Default to help.
370 GenUsage(parser, 'help')
371 return CMDhelp(parser, argv)
374 if __name__ == '__main__':
375 try:
376 sys.exit(main(sys.argv[1:]))
377 except Error as e:
378 logging.error(str(e))
379 sys.exit(1)