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.
15 import sdk_update_common
16 from sdk_update_common
import Error
20 SCRIPT_DIR
= os
.path
.dirname(os
.path
.abspath(__file__
))
21 PARENT_DIR
= os
.path
.dirname(SCRIPT_DIR
)
23 sys
.path
.append(os
.path
.dirname(SCRIPT_DIR
))
27 # Import late so each command script can find our imports
30 import command
.sources
31 import command
.uninstall
34 # This revision number is autogenerated from the Chrome revision.
35 REVISION
= '{REVISION}'
37 GSTORE_URL
= 'https://storage.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')
56 def LoadConfig(raise_on_error
=False):
57 path
= os
.path
.join(USER_DATA_DIR
, CONFIG_FILENAME
)
59 if not os
.path
.exists(path
):
67 raise Error('Unable to read config from "%s".\n %s' % (path
, e
))
70 cfg
.LoadJson(file_data
)
72 raise Error('Parsing config file from "%s" failed.\n %s' % (path
, e
))
84 path
= os
.path
.join(USER_DATA_DIR
, CONFIG_FILENAME
)
86 sdk_update_common
.MakeDirs(USER_DATA_DIR
)
87 except Exception as e
:
88 raise Error('Unable to create directory "%s".\n %s' % (USER_DATA_DIR
, e
))
90 cfg_json
= cfg
.ToJson()
93 with
open(path
, 'w') as f
:
96 raise Error('Unable to write config to "%s".\n %s' % (path
, e
))
99 def LoadLocalManifest(raise_on_error
=False):
100 path
= os
.path
.join(USER_DATA_DIR
, MANIFEST_FILENAME
)
101 manifest
= manifest_util
.SDKManifest()
104 with
open(path
) as f
:
105 manifest_string
= f
.read()
107 raise Error('Unable to read manifest from "%s".\n %s' % (path
, e
))
110 manifest
.LoadDataFromString(manifest_string
)
111 except Exception as e
:
112 raise Error('Parsing local manifest "%s" failed.\n %s' % (path
, e
))
121 def WriteLocalManifest(manifest
):
122 path
= os
.path
.join(USER_DATA_DIR
, MANIFEST_FILENAME
)
124 sdk_update_common
.MakeDirs(USER_DATA_DIR
)
125 except Exception as e
:
126 raise Error('Unable to create directory "%s".\n %s' % (USER_DATA_DIR
, e
))
129 manifest_json
= manifest
.GetDataAsString()
130 except Exception as e
:
131 raise Error('Error encoding manifest "%s" to JSON.\n %s' % (path
, e
))
134 with
open(path
, 'w') as f
:
135 f
.write(manifest_json
)
137 raise Error('Unable to write manifest to "%s".\n %s' % (path
, e
))
140 def LoadRemoteManifest(url
):
141 manifest
= manifest_util
.SDKManifest()
144 manifest_stream
= cStringIO
.StringIO()
145 url_stream
= download
.UrlOpen(url
)
146 download
.DownloadAndComputeHash(url_stream
, manifest_stream
)
147 except urllib2
.URLError
as e
:
148 raise Error('Unable to read remote manifest from URL "%s".\n %s' % (
155 manifest
.LoadDataFromString(manifest_stream
.getvalue())
157 except manifest_util
.Error
as e
:
158 raise Error('Parsing remote manifest from URL "%s" failed.\n %s' % (
162 def LoadCombinedRemoteManifest(default_manifest_url
, cfg
):
163 manifest
= LoadRemoteManifest(default_manifest_url
)
164 for source
in cfg
.sources
:
165 manifest
.MergeManifest(LoadRemoteManifest(source
))
169 # Commands #####################################################################
172 @usage('<bundle names...>')
173 def CMDinfo(parser
, args
):
174 """display information about a bundle"""
175 options
, args
= parser
.parse_args(args
)
177 parser
.error('No bundles given')
180 remote_manifest
= LoadCombinedRemoteManifest(options
.manifest_url
, cfg
)
181 command
.info
.Info(remote_manifest
, args
)
185 def CMDlist(parser
, args
):
186 """list all available bundles"""
187 parser
.add_option('-r', '--revision', action
='store_true',
188 help='display revision numbers')
189 options
, args
= parser
.parse_args(args
)
191 parser
.error('Unsupported argument(s): %s' % ', '.join(args
))
192 local_manifest
= LoadLocalManifest()
194 remote_manifest
= LoadCombinedRemoteManifest(options
.manifest_url
, cfg
)
195 command
.list.List(remote_manifest
, local_manifest
, options
.revision
)
199 @usage('<bundle names...>')
200 def CMDupdate(parser
, args
):
201 """update a bundle in the SDK to the latest version"""
202 parser
.add_option('-F', '--force', action
='store_true',
203 help='Force updating bundles that already exist. The bundle will not be '
204 'updated if the local revision matches the remote revision.')
205 options
, args
= parser
.parse_args(args
)
206 local_manifest
= LoadLocalManifest()
208 remote_manifest
= LoadCombinedRemoteManifest(options
.manifest_url
, cfg
)
211 args
= [command
.update
.RECOMMENDED
]
214 delegate
= command
.update
.RealUpdateDelegate(USER_DATA_DIR
,
215 DEFAULT_SDK_ROOT
, cfg
)
216 command
.update
.Update(delegate
, remote_manifest
, local_manifest
, args
,
219 # Always write out the local manifest, we may have successfully updated one
220 # or more bundles before failing.
222 WriteLocalManifest(local_manifest
)
224 # Log the error writing to the manifest, but propagate the original
226 logging
.error(str(e
))
231 def CMDinstall(parser
, args
):
232 """install a bundle in the SDK"""
233 # For now, forward to CMDupdate. We may want different behavior for this
234 # in the future, though...
235 return CMDupdate(parser
, args
)
238 @usage('<bundle names...>')
239 def CMDuninstall(parser
, args
):
240 """uninstall the given bundles"""
241 _
, args
= parser
.parse_args(args
)
243 parser
.error('No bundles given')
245 local_manifest
= LoadLocalManifest()
246 command
.uninstall
.Uninstall(DEFAULT_SDK_ROOT
, local_manifest
, args
)
247 WriteLocalManifest(local_manifest
)
251 @usage('<bundle names...>')
252 def CMDreinstall(parser
, args
):
253 """restore the given bundles to their original state
255 Note that if there is an update to a given bundle, reinstall will not
256 automatically update to the newest version.
258 _
, args
= parser
.parse_args(args
)
259 local_manifest
= LoadLocalManifest()
262 parser
.error('No bundles given')
267 delegate
= command
.update
.RealUpdateDelegate(USER_DATA_DIR
,
268 DEFAULT_SDK_ROOT
, cfg
)
269 command
.update
.Reinstall(delegate
, local_manifest
, args
)
271 # Always write out the local manifest, we may have successfully updated one
272 # or more bundles before failing.
274 WriteLocalManifest(local_manifest
)
276 # Log the error writing to the manifest, but propagate the original
278 logging
.error(str(e
))
283 def CMDsources(parser
, args
):
284 """manage external package sources"""
285 parser
.add_option('-a', '--add', dest
='url_to_add',
286 help='Add an additional package source')
288 '-r', '--remove', dest
='url_to_remove',
289 help='Remove package source (use \'all\' for all additional sources)')
290 parser
.add_option('-l', '--list', dest
='do_list', action
='store_true',
291 help='List additional package sources')
292 options
, args
= parser
.parse_args(args
)
294 cfg
= LoadConfig(True)
296 if options
.url_to_add
:
297 command
.sources
.AddSource(cfg
, options
.url_to_add
)
299 elif options
.url_to_remove
:
300 command
.sources
.RemoveSource(cfg
, options
.url_to_remove
)
302 elif options
.do_list
:
303 command
.sources
.ListSources(cfg
)
313 def CMDversion(parser
, args
):
314 """display version information"""
315 _
, _
= parser
.parse_args(args
)
316 print "Native Client SDK Updater, version r%s" % REVISION
320 def CMDhelp(parser
, args
):
321 """print list of commands or help for a specific command"""
322 _
, args
= parser
.parse_args(args
)
324 return main(args
+ ['--help'])
330 return globals().get('CMD' + name
, None)
333 def GenUsage(parser
, cmd
):
334 """Modify an OptParse object with the function's documentation."""
336 more
= getattr(obj
, 'usage_more', '')
340 # OptParser.description prefer nicely non-formatted strings.
341 parser
.description
= re
.sub('[\r\n ]{2,}', ' ', obj
.__doc
__)
342 parser
.set_usage('usage: %%prog %s [options] %s' % (cmd
, more
))
345 def UpdateSDKTools(options
, args
):
346 """update the sdk_tools bundle"""
348 local_manifest
= LoadLocalManifest()
350 remote_manifest
= LoadCombinedRemoteManifest(options
.manifest_url
, cfg
)
353 delegate
= command
.update
.RealUpdateDelegate(USER_DATA_DIR
,
354 DEFAULT_SDK_ROOT
, cfg
)
355 command
.update
.UpdateBundleIfNeeded(
359 command
.update
.SDK_TOOLS
,
362 # Always write out the local manifest, we may have successfully updated one
363 # or more bundles before failing.
364 WriteLocalManifest(local_manifest
)
369 # Get all commands...
370 cmds
= [fn
[3:] for fn
in dir(sys
.modules
[__name__
]) if fn
.startswith('CMD')]
371 # Remove hidden commands...
372 cmds
= filter(lambda fn
: not getattr(Command(fn
), 'hide', 0), cmds
)
373 # Format for CMDhelp usage.
374 CMDhelp
.usage_more
= ('\n\nCommands are:\n' + '\n'.join([
375 ' %-10s %s' % (fn
, Command(fn
).__doc
__.split('\n')[0].strip())
378 # Create the option parse and add --verbose support.
379 parser
= optparse
.OptionParser()
381 '-v', '--verbose', action
='count', default
=0,
382 help='Use 2 times for more debugging info')
383 parser
.add_option('-U', '--manifest-url', dest
='manifest_url',
384 default
=GSTORE_URL
+ '/nacl/nacl_sdk/' + MANIFEST_FILENAME
,
385 metavar
='URL', help='override the default URL for the NaCl manifest file')
386 parser
.add_option('--update-sdk-tools', action
='store_true',
387 dest
='update_sdk_tools', help=optparse
.SUPPRESS_HELP
)
389 old_parser_args
= parser
.parse_args
391 options
, args
= old_parser_args(args
)
392 if options
.verbose
>= 2:
393 loglevel
= logging
.DEBUG
394 elif options
.verbose
:
395 loglevel
= logging
.INFO
397 loglevel
= logging
.WARNING
399 fmt
= '%(levelname)s:%(message)s'
400 logging
.basicConfig(stream
=sys
.stdout
, level
=loglevel
, format
=fmt
)
402 # If --update-sdk-tools is passed, circumvent any other command running.
403 if options
.update_sdk_tools
:
404 UpdateSDKTools(options
, args
)
408 parser
.parse_args
= Parse
411 cmd
= Command(argv
[0])
413 # "fix" the usage and the description now that we know the subcommand.
414 GenUsage(parser
, argv
[0])
415 return cmd(parser
, argv
[1:])
417 # Not a known command. Default to help.
418 GenUsage(parser
, 'help')
419 return CMDhelp(parser
, argv
)
422 if __name__
== '__main__':
424 sys
.exit(main(sys
.argv
[1:]))
426 logging
.error(str(e
))
428 except KeyboardInterrupt:
429 sys
.stderr
.write('naclsdk: interrupted\n')