| Index: native_client_sdk/src/build_tools/sdk_tools/command/update.py
|
| diff --git a/native_client_sdk/src/build_tools/sdk_tools/command/update.py b/native_client_sdk/src/build_tools/sdk_tools/command/update.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..24389c93737a61c2e656a92d4b4f0a3e78931bf3
|
| --- /dev/null
|
| +++ b/native_client_sdk/src/build_tools/sdk_tools/command/update.py
|
| @@ -0,0 +1,238 @@
|
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +import download
|
| +import logging
|
| +import os
|
| +from sdk_update_common import Error
|
| +import sdk_update_common
|
| +import sys
|
| +import urlparse
|
| +import urllib2
|
| +
|
| +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
| +PARENT_DIR = os.path.dirname(SCRIPT_DIR)
|
| +sys.path.append(PARENT_DIR)
|
| +try:
|
| + import cygtar
|
| +except ImportError:
|
| + # Try to find this in the Chromium repo.
|
| + CHROME_SRC_DIR = os.path.abspath(
|
| + os.path.join(PARENT_DIR, '..', '..', '..', '..'))
|
| + sys.path.append(os.path.join(CHROME_SRC_DIR, 'native_client', 'build'))
|
| + import cygtar
|
| +
|
| +
|
| +RECOMMENDED = 'recommended'
|
| +SDK_TOOLS = 'sdk_tools'
|
| +HTTP_CONTENT_LENGTH = 'Content-Length' # HTTP Header field for content length
|
| +
|
| +
|
| +class UpdateDelegate(object):
|
| + def BundleDirectoryExists(self, bundle_name):
|
| + raise NotImplementedError()
|
| +
|
| + def DownloadToFile(self, url, dest_filename):
|
| + raise NotImplementedError()
|
| +
|
| + def ExtractArchive(self, archive, extract_dir, rename_from_dir,
|
| + rename_to_dir):
|
| + raise NotImplementedError()
|
| +
|
| +
|
| +class RealUpdateDelegate(UpdateDelegate):
|
| + def __init__(self, user_data_dir, install_dir):
|
| + UpdateDelegate.__init__(self)
|
| + self.user_data_dir = user_data_dir
|
| + self.install_dir = install_dir
|
| +
|
| + def BundleDirectoryExists(self, bundle_name):
|
| + bundle_path = os.path.join(self.install_dir, bundle_name)
|
| + return os.path.isdir(bundle_path)
|
| +
|
| + def DownloadToFile(self, url, dest_filename):
|
| + sdk_update_common.MakeDirs(self.user_data_dir)
|
| + dest_path = os.path.join(self.user_data_dir, dest_filename)
|
| + out_stream = None
|
| + url_stream = None
|
| + try:
|
| + out_stream = open(dest_path, 'wb')
|
| + url_stream = download.UrlOpen(url)
|
| + content_length = int(url_stream.info()[HTTP_CONTENT_LENGTH])
|
| + progress = download.MakeProgressFunction(content_length)
|
| + sha1, size = download.DownloadAndComputeHash(url_stream, out_stream,
|
| + progress)
|
| + return sha1, size
|
| + except urllib2.URLError as e:
|
| + raise Error('Unable to read from URL "%s".\n %s' % (url, e))
|
| + except IOError as e:
|
| + raise Error('Unable to write to file "%s".\n %s' % (dest_filename, e))
|
| + finally:
|
| + if url_stream:
|
| + url_stream.close()
|
| + if out_stream:
|
| + out_stream.close()
|
| +
|
| + def ExtractArchive(self, archive, extract_dir, rename_from_dir,
|
| + rename_to_dir):
|
| + tar_file = None
|
| +
|
| + archive_path = os.path.join(self.user_data_dir, archive)
|
| + extract_path = os.path.join(self.install_dir, extract_dir)
|
| + rename_from_path = os.path.join(self.install_dir, rename_from_dir)
|
| + rename_to_path = os.path.join(self.install_dir, rename_to_dir)
|
| +
|
| + # Extract to extract_dir, usually "<bundle name>_update".
|
| + # This way if the extraction fails, we haven't blown away the old bundle
|
| + # (if it exists).
|
| + sdk_update_common.RemoveDir(extract_path)
|
| + sdk_update_common.MakeDirs(extract_path)
|
| + curpath = os.getcwd()
|
| + tar_file = None
|
| +
|
| + try:
|
| + try:
|
| + tar_file = cygtar.CygTar(archive_path, 'r', verbose=True)
|
| + except Exception as e:
|
| + raise Error('Can\'t open archive "%s".\n %s' % (archive_path, e))
|
| +
|
| + try:
|
| + logging.info('Changing the directory to %s' % (extract_path,))
|
| + os.chdir(extract_path)
|
| + except Exception as e:
|
| + raise Error('Unable to chdir into "%s".\n %s' % (extract_path, e))
|
| +
|
| + logging.info('Extracting to %s' % (extract_path,))
|
| + tar_file.Extract()
|
| +
|
| + logging.info('Changing the directory to %s' % (curpath,))
|
| + os.chdir(curpath)
|
| +
|
| + logging.info('Renaming %s->%s' % (rename_from_path, rename_to_path))
|
| + sdk_update_common.RenameDir(rename_from_path, rename_to_path)
|
| + finally:
|
| + # Change the directory back so we can remove the update directory.
|
| + os.chdir(curpath)
|
| +
|
| + # Clean up the ..._update directory.
|
| + try:
|
| + sdk_update_common.RemoveDir(extract_path)
|
| + except Exception as e:
|
| + logging.error('Failed to remove directory \"%s\". %s' % (
|
| + extract_path, e))
|
| +
|
| + if tar_file:
|
| + tar_file.Close()
|
| +
|
| + # Remove the archive.
|
| + os.remove(archive_path)
|
| +
|
| +
|
| +def Update(delegate, remote_manifest, local_manifest, bundle_names, force):
|
| + valid_bundles = set([bundle.name for bundle in remote_manifest.GetBundles()])
|
| + requested_bundles = _GetRequestedBundlesFromArgs(remote_manifest,
|
| + bundle_names)
|
| + invalid_bundles = requested_bundles - valid_bundles
|
| + if invalid_bundles:
|
| + logging.warn('Ignoring unknown bundle(s): %s' % (
|
| + ', '.join(invalid_bundles)))
|
| + requested_bundles -= invalid_bundles
|
| +
|
| + if SDK_TOOLS in requested_bundles:
|
| + logging.warn('Updating sdk_tools happens automatically. '
|
| + 'Ignoring manual update request.')
|
| + requested_bundles.discard(SDK_TOOLS)
|
| +
|
| + if requested_bundles:
|
| + for bundle_name in requested_bundles:
|
| + logging.info('Trying to update %s' % (bundle_name,))
|
| + UpdateBundleIfNeeded(delegate, remote_manifest, local_manifest,
|
| + bundle_name, force)
|
| + else:
|
| + logging.warn('No bundles to update.')
|
| +
|
| +
|
| +def UpdateBundleIfNeeded(delegate, remote_manifest, local_manifest,
|
| + bundle_name, force):
|
| + bundle = remote_manifest.GetBundle(bundle_name)
|
| + if bundle:
|
| + if _BundleNeedsUpdate(delegate, local_manifest, bundle):
|
| + _UpdateBundle(delegate, bundle, local_manifest, force)
|
| + else:
|
| + print '%s is already up-to-date.' % (bundle.name,)
|
| + else:
|
| + logging.error('Bundle %s does not exist.' % (bundle_name,))
|
| +
|
| +
|
| +def _GetRequestedBundlesFromArgs(remote_manifest, requested_bundles):
|
| + requested_bundles = set(requested_bundles)
|
| + if RECOMMENDED in requested_bundles:
|
| + requested_bundles.discard(RECOMMENDED)
|
| + requested_bundles |= set(_GetRecommendedBundles(remote_manifest))
|
| +
|
| + return requested_bundles
|
| +
|
| +
|
| +def _GetRecommendedBundles(remote_manifest):
|
| + return [bundle for bundle in remote_manifest.GetBundles() if
|
| + bundle.recommended]
|
| +
|
| +
|
| +def _BundleNeedsUpdate(delegate, local_manifest, bundle):
|
| + # Always update the bundle if the directory doesn't exist;
|
| + # the user may have deleted it.
|
| + if not delegate.BundleDirectoryExists(bundle.name):
|
| + return True
|
| +
|
| + return local_manifest.BundleNeedsUpdate(bundle)
|
| +
|
| +
|
| +def _UpdateBundle(delegate, bundle, local_manifest, force):
|
| + archive = bundle.GetHostOSArchive()
|
| + if not archive:
|
| + logging.warn('Bundle %s does not exist for this platform.' % (bundle.name,))
|
| + return
|
| +
|
| + print 'Downloading bundle %s' % (bundle.name,)
|
| + dest_filename = _GetFilenameFromURL(archive.url)
|
| + sha1, size = delegate.DownloadToFile(archive.url, dest_filename)
|
| + _ValidateArchive(archive, sha1, size)
|
| +
|
| + print 'Updating bundle %s to version %s, revision %s' % (
|
| + bundle.name, bundle.version, bundle.revision)
|
| + extract_dir = bundle.name + '_update'
|
| +
|
| + repath_dir = bundle.get('repath', None)
|
| + if repath_dir:
|
| + # If repath is specified:
|
| + # The files are extracted to nacl_sdk/<bundle.name>_update/<repath>/...
|
| + # The destination directory is nacl_sdk/<repath>/...
|
| + rename_from_dir = os.path.join(extract_dir, repath_dir)
|
| + rename_to_dir = repath_dir
|
| + else:
|
| + # If no repath is specified:
|
| + # The files are extracted to nacl_sdk/<bundle.name>_update/...
|
| + # The destination directory is nacl_sdk/<bundle.name>/...
|
| + rename_from_dir = extract_dir
|
| + rename_to_dir = bundle.name
|
| +
|
| + delegate.ExtractArchive(dest_filename, extract_dir, rename_from_dir,
|
| + rename_to_dir)
|
| +
|
| + logging.info('Updating local manifest to include bundle %s' % (bundle.name))
|
| + local_manifest.MergeBundle(bundle)
|
| +
|
| +
|
| +def _GetFilenameFromURL(url):
|
| + _, _, path, _, _, _ = urlparse.urlparse(url)
|
| + return path.split('/')[-1]
|
| +
|
| +
|
| +def _ValidateArchive(archive, actual_sha1, actual_size):
|
| + if actual_sha1 != archive.GetChecksum():
|
| + raise Error('SHA1 checksum mismatch on "%s". Expected %s but got %s' % (
|
| + archive.name, archive.GetChecksum(), actual_sha1))
|
| + if actual_size != archive.size:
|
| + raise Error('Size mismatch on "%s". Expected %s but got %s bytes' % (
|
| + archive.name, archive.size, actual_size))
|
|
|