| Index: build/android/play_services/update.py
|
| diff --git a/build/android/play_services/update.py b/build/android/play_services/update.py
|
| index b2d57ad359f49c7c271246d4f0b629b332b551d0..c6c8dcc1f13847e85b83f7e86df1049c17dd2f3e 100755
|
| --- a/build/android/play_services/update.py
|
| +++ b/build/android/play_services/update.py
|
| @@ -4,12 +4,11 @@
|
| # found in the LICENSE file.
|
|
|
| '''
|
| -Script to help uploading and downloading the Google Play services client
|
| -library to and from a Google Cloud storage.
|
| +Script to help uploading and downloading the Google Play services library to
|
| +and from a Google Cloud storage.
|
| '''
|
|
|
| import argparse
|
| -import collections
|
| import logging
|
| import os
|
| import re
|
| @@ -37,7 +36,7 @@ SHA1_DIRECTORY = os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android',
|
| 'play_services')
|
|
|
| # Default bucket used for storing the files.
|
| -GMS_CLOUD_STORAGE = 'chrome-sdk-extras'
|
| +GMS_CLOUD_STORAGE = 'chromium-android-tools/play-services'
|
|
|
| # Path to the default configuration file. It exposes the currently installed
|
| # version of the library in a human readable way.
|
| @@ -45,13 +44,13 @@ CONFIG_DEFAULT_PATH = os.path.join(constants.DIR_SOURCE_ROOT, 'build',
|
| 'android', 'play_services', 'config.json')
|
|
|
| LICENSE_FILE_NAME = 'LICENSE'
|
| -LIBRARY_FILE_NAME = 'google_play_services_library.zip'
|
| +ZIP_FILE_NAME = 'google_play_services_library.zip'
|
| GMS_PACKAGE_ID = 'extra-google-google_play_services' # used by sdk manager
|
|
|
| LICENSE_PATTERN = re.compile(r'^Pkg\.License=(?P<text>.*)$', re.MULTILINE)
|
|
|
|
|
| -def Main():
|
| +def main(raw_args):
|
| parser = argparse.ArgumentParser(
|
| description=__doc__ + 'Please see the subcommand help for more details.',
|
| formatter_class=utils.DefaultsRawHelpFormatter)
|
| @@ -63,27 +62,18 @@ def Main():
|
| help='download the library from the cloud storage',
|
| description=Download.__doc__,
|
| formatter_class=utils.DefaultsRawHelpFormatter)
|
| - parser_download.add_argument('-f', '--force',
|
| - action='store_true',
|
| - help=('run even if the local version is '
|
| - 'already up to date'))
|
| parser_download.set_defaults(func=Download)
|
| - AddCommonArguments(parser_download)
|
| + AddBasicArguments(parser_download)
|
| + AddBucketArguments(parser_download)
|
|
|
| # SDK Update arguments
|
| parser_sdk = subparsers.add_parser(
|
| 'sdk',
|
| - help='update the local sdk using the Android SDK Manager',
|
| + help='get the latest Google Play services SDK using Android SDK Manager',
|
| description=UpdateSdk.__doc__,
|
| formatter_class=utils.DefaultsRawHelpFormatter)
|
| - parser_sdk.add_argument('--sdk-root',
|
| - help=('base path to the Android SDK tools to use to '
|
| - 'update the library'),
|
| - default=constants.ANDROID_SDK_ROOT)
|
| - parser_sdk.add_argument('-v', '--verbose',
|
| - action='store_true',
|
| - help='print debug information')
|
| parser_sdk.set_defaults(func=UpdateSdk)
|
| + AddBasicArguments(parser_sdk)
|
|
|
| # Upload arguments
|
| parser_upload = subparsers.add_parser(
|
| @@ -91,70 +81,90 @@ def Main():
|
| help='upload the library to the cloud storage',
|
| description=Upload.__doc__,
|
| formatter_class=utils.DefaultsRawHelpFormatter)
|
| - parser_upload.add_argument('-f', '--force',
|
| - action='store_true',
|
| - help=('run even if the checked in version is '
|
| - 'already up to date'))
|
| - parser_upload.add_argument('--sdk-root',
|
| - help=('base path to the Android SDK tools to use '
|
| - 'to update the library'),
|
| - default=constants.ANDROID_SDK_ROOT)
|
| +
|
| parser_upload.add_argument('--skip-git',
|
| action='store_true',
|
| help="don't commit the changes at the end")
|
| parser_upload.set_defaults(func=Upload)
|
| - AddCommonArguments(parser_upload)
|
| + AddBasicArguments(parser_upload)
|
| + AddBucketArguments(parser_upload)
|
|
|
| - args = parser.parse_args()
|
| + args = parser.parse_args(raw_args)
|
| if args.verbose:
|
| logging.basicConfig(level=logging.DEBUG)
|
| - logging_utils.ColorStreamHandler.MakeDefault()
|
| + logging_utils.ColorStreamHandler.MakeDefault(not _IsBotEnvironment())
|
| return args.func(args)
|
|
|
|
|
| -def AddCommonArguments(parser):
|
| +def AddBasicArguments(parser):
|
| '''
|
| Defines the common arguments on subparser rather than the main one. This
|
| allows to put arguments after the command: `foo.py upload --debug --force`
|
| instead of `foo.py --debug upload --force`
|
| '''
|
|
|
| + parser.add_argument('--sdk-root',
|
| + help='base path to the Android SDK tools root',
|
| + default=constants.ANDROID_SDK_ROOT)
|
| +
|
| + parser.add_argument('-v', '--verbose',
|
| + action='store_true',
|
| + help='print debug information')
|
| +
|
| +
|
| +def AddBucketArguments(parser):
|
| parser.add_argument('--bucket',
|
| help='name of the bucket where the files are stored',
|
| default=GMS_CLOUD_STORAGE)
|
| +
|
| parser.add_argument('--config',
|
| help='JSON Configuration file',
|
| default=CONFIG_DEFAULT_PATH)
|
| +
|
| parser.add_argument('--dry-run',
|
| action='store_true',
|
| help=('run the script in dry run mode. Files will be '
|
| - 'copied to a local directory instead of the cloud '
|
| - 'storage. The bucket name will be as path to that '
|
| - 'directory relative to the repository root.'))
|
| - parser.add_argument('-v', '--verbose',
|
| + 'copied to a local directory instead of the '
|
| + 'cloud storage. The bucket name will be as path '
|
| + 'to that directory relative to the repository '
|
| + 'root.'))
|
| +
|
| + parser.add_argument('-f', '--force',
|
| action='store_true',
|
| - help='print debug information')
|
| + help='run even if the library is already up to date')
|
|
|
|
|
| def Download(args):
|
| '''
|
| - Downloads the Google Play services client library from a Google Cloud Storage
|
| - bucket and installs it to
|
| + Downloads the Google Play services library from a Google Cloud Storage bucket
|
| + and installs it to
|
| //third_party/android_tools/sdk/extras/google/google_play_services.
|
|
|
| A license check will be made, and the user might have to accept the license
|
| if that has not been done before.
|
| '''
|
|
|
| - paths = _InitPaths(constants.ANDROID_SDK_ROOT)
|
| + if not os.path.isdir(args.sdk_root):
|
| + logging.debug('Did not find the Android SDK root directory at "%s".',
|
| + args.sdk_root)
|
| + if not args.force:
|
| + logging.info('Skipping, not on an android checkout.')
|
| + return 0
|
| +
|
| + paths = PlayServicesPaths(args.sdk_root)
|
|
|
| - new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY, LIBRARY_FILE_NAME + '.sha1')
|
| - old_lib_zip_sha1 = os.path.join(paths.package, LIBRARY_FILE_NAME + '.sha1')
|
| + if os.path.isdir(paths.package) and not os.access(paths.package, os.W_OK):
|
| + logging.error('Failed updating the Google Play Services library. '
|
| + 'The location is not writable. Please remove the '
|
| + 'directory (%s) and try again.', paths.package)
|
| + return -2
|
|
|
| - logging.debug('Comparing library hashes: %s and %s', new_lib_zip_sha1,
|
| - old_lib_zip_sha1)
|
| - if utils.FileEquals(new_lib_zip_sha1, old_lib_zip_sha1) and not args.force:
|
| - logging.debug('The Google Play services library is up to date.')
|
| + new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY, ZIP_FILE_NAME + '.sha1')
|
| +
|
| + logging.debug('Comparing zip hashes: %s and %s', new_lib_zip_sha1,
|
| + paths.lib_zip_sha1)
|
| + if utils.FileEquals(new_lib_zip_sha1, paths.lib_zip_sha1) and not args.force:
|
| + logging.info('Skipping, the Google Play services library is up to date.')
|
| return 0
|
|
|
| config = utils.ConfigParser(args.config)
|
| @@ -164,43 +174,53 @@ def Download(args):
|
|
|
| tmp_root = tempfile.mkdtemp()
|
| try:
|
| - if not os.environ.get('CHROME_HEADLESS'):
|
| - if not os.path.isdir(paths.package):
|
| - os.makedirs(paths.package)
|
| -
|
| - # download license file from bucket/{version_number}/license.sha1
|
| - new_license = os.path.join(tmp_root, LICENSE_FILE_NAME)
|
| - old_license = os.path.join(paths.package, LICENSE_FILE_NAME)
|
| -
|
| - license_sha1 = os.path.join(SHA1_DIRECTORY, LICENSE_FILE_NAME + '.sha1')
|
| - _DownloadFromBucket(bucket_path, license_sha1, new_license,
|
| - args.verbose, args.dry_run)
|
| - if not _CheckLicenseAgreement(new_license, old_license):
|
| + # setup the destination directory
|
| + if not os.path.isdir(paths.package):
|
| + os.makedirs(paths.package)
|
| +
|
| + # download license file from bucket/{version_number}/license.sha1
|
| + new_license = os.path.join(tmp_root, LICENSE_FILE_NAME)
|
| +
|
| + license_sha1 = os.path.join(SHA1_DIRECTORY, LICENSE_FILE_NAME + '.sha1')
|
| + _DownloadFromBucket(bucket_path, license_sha1, new_license,
|
| + args.verbose, args.dry_run)
|
| +
|
| + if (not _IsBotEnvironment() and
|
| + not _CheckLicenseAgreement(new_license, paths.license,
|
| + config.version_number)):
|
| logging.warning('Your version of the Google Play services library is '
|
| 'not up to date. You might run into issues building '
|
| 'or running the app. Please run `%s download` to '
|
| 'retry downloading it.', __file__)
|
| return 0
|
|
|
| - new_lib_zip = os.path.join(tmp_root, LIBRARY_FILE_NAME)
|
| + new_lib_zip = os.path.join(tmp_root, ZIP_FILE_NAME)
|
| _DownloadFromBucket(bucket_path, new_lib_zip_sha1, new_lib_zip,
|
| args.verbose, args.dry_run)
|
|
|
| - # We remove only the library itself. Users having a SDK manager installed
|
| - # library before will keep the documentation and samples from it.
|
| - shutil.rmtree(paths.lib, ignore_errors=True)
|
| - os.makedirs(paths.lib)
|
| + try:
|
| + # We remove the current version of the Google Play services SDK.
|
| + if os.path.exists(paths.package):
|
| + shutil.rmtree(paths.package)
|
| + os.makedirs(paths.package)
|
| +
|
| + logging.debug('Extracting the library to %s', paths.lib)
|
| + with zipfile.ZipFile(new_lib_zip, "r") as new_lib_zip_file:
|
| + new_lib_zip_file.extractall(paths.lib)
|
|
|
| - logging.debug('Extracting the library to %s', paths.lib)
|
| - with zipfile.ZipFile(new_lib_zip, "r") as new_lib_zip_file:
|
| - new_lib_zip_file.extractall(paths.lib)
|
| + logging.debug('Copying %s to %s', new_license, paths.license)
|
| + shutil.copy(new_license, paths.license)
|
|
|
| - logging.debug('Copying %s to %s', new_license, old_license)
|
| - shutil.copy(new_license, old_license)
|
| + logging.debug('Copying %s to %s', new_lib_zip_sha1, paths.lib_zip_sha1)
|
| + shutil.copy(new_lib_zip_sha1, paths.lib_zip_sha1)
|
|
|
| - logging.debug('Copying %s to %s', new_lib_zip_sha1, old_lib_zip_sha1)
|
| - shutil.copy(new_lib_zip_sha1, old_lib_zip_sha1)
|
| + logging.info('Update complete.')
|
|
|
| + except Exception as e: # pylint: disable=broad-except
|
| + logging.error('Failed updating the Google Play Services library. '
|
| + 'An error occurred while installing the new version in '
|
| + 'the SDK directory: %s ', e)
|
| + return -3
|
| finally:
|
| shutil.rmtree(tmp_root)
|
|
|
| @@ -209,8 +229,8 @@ def Download(args):
|
|
|
| def UpdateSdk(args):
|
| '''
|
| - Uses the Android SDK Manager to update or download the local Google Play
|
| - services library. Its usual installation path is
|
| + Uses the Android SDK Manager to download the latest Google Play services SDK
|
| + locally. Its usual installation path is
|
| //third_party/android_tools/sdk/extras/google/google_play_services
|
| '''
|
|
|
| @@ -229,7 +249,7 @@ def UpdateSdk(args):
|
|
|
| def Upload(args):
|
| '''
|
| - Uploads the local Google Play services client library to a Google Cloud
|
| + Uploads the library from the local Google Play services SDK to a Google Cloud
|
| storage bucket.
|
|
|
| By default, a local commit will be made at the end of the operation.
|
| @@ -240,7 +260,7 @@ def Upload(args):
|
| # disable breakpad to avoid spamming the logs.
|
| breakpad.IS_ENABLED = False
|
|
|
| - paths = _InitPaths(args.sdk_root)
|
| + paths = PlayServicesPaths(args.sdk_root)
|
|
|
| if not args.skip_git and utils.IsRepoDirty(constants.DIR_SOURCE_ROOT):
|
| logging.error('The repo is dirty. Please commit or stash your changes.')
|
| @@ -248,24 +268,24 @@ def Upload(args):
|
|
|
| config = utils.ConfigParser(args.config)
|
|
|
| - version_xml = os.path.join(paths.lib, 'res', 'values', 'version.xml')
|
| - new_version_number = utils.GetVersionNumberFromLibraryResources(version_xml)
|
| + new_version_number = utils.GetVersionNumberFromLibraryResources(
|
| + paths.version_xml)
|
| logging.debug('comparing versions: new=%d, old=%s',
|
| new_version_number, config.version_number)
|
| if new_version_number <= config.version_number and not args.force:
|
| logging.info('The checked in version of the library is already the latest '
|
| - 'one. No update needed. Please rerun with --force to skip '
|
| + 'one. No update is needed. Please rerun with --force to skip '
|
| 'this check.')
|
| return 0
|
|
|
| tmp_root = tempfile.mkdtemp()
|
| try:
|
| - new_lib_zip = os.path.join(tmp_root, LIBRARY_FILE_NAME)
|
| + new_lib_zip = os.path.join(tmp_root, ZIP_FILE_NAME)
|
| new_license = os.path.join(tmp_root, LICENSE_FILE_NAME)
|
|
|
| # need to strip '.zip' from the file name here
|
| shutil.make_archive(new_lib_zip[:-4], 'zip', paths.lib)
|
| - _ExtractLicenseFile(new_license, paths.package)
|
| + _ExtractLicenseFile(new_license, paths.source_prop)
|
|
|
| bucket_path = _VerifyBucketPathFormat(args.bucket, new_version_number,
|
| args.dry_run)
|
| @@ -274,7 +294,7 @@ def Upload(args):
|
| _UploadToBucket(bucket_path, files_to_upload, args.dry_run)
|
|
|
| new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY,
|
| - LIBRARY_FILE_NAME + '.sha1')
|
| + ZIP_FILE_NAME + '.sha1')
|
| new_license_sha1 = os.path.join(SHA1_DIRECTORY,
|
| LICENSE_FILE_NAME + '.sha1')
|
| shutil.copy(new_lib_zip + '.sha1', new_lib_zip_sha1)
|
| @@ -294,35 +314,6 @@ def Upload(args):
|
| return 0
|
|
|
|
|
| -def _InitPaths(sdk_root):
|
| - '''
|
| - Initializes the different paths to be used in the update process.
|
| - '''
|
| -
|
| - PlayServicesPaths = collections.namedtuple('PlayServicesPaths', [
|
| - # Android SDK root path
|
| - 'sdk_root',
|
| -
|
| - # Path to the Google Play services package in the SDK manager sense,
|
| - # where it installs the source.properties file
|
| - 'package',
|
| -
|
| - # Path to the Google Play services library itself (jar and res)
|
| - 'lib',
|
| - ])
|
| -
|
| - sdk_play_services_package_dir = os.path.join('extras', 'google',
|
| - 'google_play_services')
|
| - sdk_play_services_lib_dir = os.path.join(sdk_play_services_package_dir,
|
| - 'libproject',
|
| - 'google-play-services_lib')
|
| -
|
| - return PlayServicesPaths(
|
| - sdk_root=sdk_root,
|
| - package=os.path.join(sdk_root, sdk_play_services_package_dir),
|
| - lib=os.path.join(sdk_root, sdk_play_services_lib_dir))
|
| -
|
| -
|
| def _DownloadFromBucket(bucket_path, sha1_file, destination, verbose,
|
| is_dry_run):
|
| '''Downloads the file designated by the provided sha1 from a cloud bucket.'''
|
| @@ -367,8 +358,7 @@ def _InitGsutil(is_dry_run):
|
| download_from_google_storage.GSUTIL_DEFAULT_PATH)
|
|
|
|
|
| -def _ExtractLicenseFile(license_path, play_services_package_dir):
|
| - prop_file_path = os.path.join(play_services_package_dir, 'source.properties')
|
| +def _ExtractLicenseFile(license_path, prop_file_path):
|
| with open(prop_file_path, 'r') as prop_file:
|
| prop_file_content = prop_file.read()
|
|
|
| @@ -381,7 +371,8 @@ def _ExtractLicenseFile(license_path, play_services_package_dir):
|
| license_file.write(match.group('text'))
|
|
|
|
|
| -def _CheckLicenseAgreement(expected_license_path, actual_license_path):
|
| +def _CheckLicenseAgreement(expected_license_path, actual_license_path,
|
| + version_number):
|
| '''
|
| Checks that the new license is the one already accepted by the user. If it
|
| isn't, it prompts the user to accept it. Returns whether the expected license
|
| @@ -392,13 +383,16 @@ def _CheckLicenseAgreement(expected_license_path, actual_license_path):
|
| return True
|
|
|
| with open(expected_license_path) as license_file:
|
| + # Uses plain print rather than logging to make sure this is not formatted
|
| + # by the logger.
|
| + print ('Updating the Google Play services SDK to '
|
| + 'version %d.' % version_number)
|
| +
|
| # The output is buffered when running as part of gclient hooks. We split
|
| # the text here and flush is explicitly to avoid having part of it dropped
|
| # out.
|
| # Note: text contains *escaped* new lines, so we split by '\\n', not '\n'.
|
| for license_part in license_file.read().split('\\n'):
|
| - # Uses plain print rather than logging to make sure this is not formatted
|
| - # by the logger.
|
| print license_part
|
| sys.stdout.flush()
|
|
|
| @@ -409,6 +403,10 @@ def _CheckLicenseAgreement(expected_license_path, actual_license_path):
|
| return raw_input('> ') in ('Y', 'y')
|
|
|
|
|
| +def _IsBotEnvironment():
|
| + return bool(os.environ.get('CHROME_HEADLESS'))
|
| +
|
| +
|
| def _VerifyBucketPathFormat(bucket_name, version_number, is_dry_run):
|
| '''
|
| Formats and checks the download/upload path depending on whether we are
|
| @@ -432,6 +430,64 @@ def _VerifyBucketPathFormat(bucket_name, version_number, is_dry_run):
|
| return bucket_path
|
|
|
|
|
| +class PlayServicesPaths(object):
|
| + '''
|
| + Describes the different paths to be used in the update process.
|
| +
|
| + Filesystem hierarchy | Exposed property / notes
|
| + ---------------------------------------------------|-------------------------
|
| + [sdk_root] | sdk_root / (1)
|
| + +- extras |
|
| + +- google |
|
| + +- google_play_services | package / (2)
|
| + +- source.properties | source_prop / (3)
|
| + +- LICENSE | license / (4)
|
| + +- google_play_services_library.zip.sha1 | lib_zip_sha1 / (5)
|
| + +- libproject |
|
| + +- google-play-services_lib | lib / (6)
|
| + +- res |
|
| + +- values |
|
| + +- version.xml | version_xml (7)
|
| +
|
| + Notes:
|
| +
|
| + 1. sdk_root: Path provided as a parameter to the script (--sdk_root)
|
| + 2. package: This directory contains the Google Play services SDK itself.
|
| + When downloaded via the Android SDK manager, it will contain,
|
| + documentation, samples and other files in addition to the library. When
|
| + the update script downloads the library from our cloud storage, it is
|
| + cleared.
|
| + 3. source_prop: File created by the Android SDK manager that contains
|
| + the package information, such as the version info and the license.
|
| + 4. license: File created by the update script. Contains the license accepted
|
| + by the user.
|
| + 5. lib_zip_sha1: sha1 of the library zip that has been installed by the
|
| + update script. It is compared with the one required by the config file to
|
| + check if an update is necessary.
|
| + 6. lib: Contains the library itself: jar and resources. This is what is
|
| + downloaded from the cloud storage.
|
| + 7. version_xml: File that contains the exact Google Play services library
|
| + version, the one that we track. The version looks like 811500, is used in
|
| + the code and the on-device APK, as opposed to the SDK package version
|
| + which looks like 27.0.0 and is used only by the Android SDK manager.
|
| +
|
| + '''
|
| +
|
| + def __init__(self, sdk_root):
|
| + relative_package = os.path.join('extras', 'google', 'google_play_services')
|
| + relative_lib = os.path.join(relative_package, 'libproject',
|
| + 'google-play-services_lib')
|
| + self.sdk_root = sdk_root
|
| +
|
| + self.package = os.path.join(sdk_root, relative_package)
|
| + self.lib_zip_sha1 = os.path.join(self.package, ZIP_FILE_NAME + '.sha1')
|
| + self.license = os.path.join(self.package, LICENSE_FILE_NAME)
|
| + self.source_prop = os.path.join(self.package, 'source.properties')
|
| +
|
| + self.lib = os.path.join(sdk_root, relative_lib)
|
| + self.version_xml = os.path.join(self.lib, 'res', 'values', 'version.xml')
|
| +
|
| +
|
| class DummyGsutil(download_from_google_storage.Gsutil):
|
| '''
|
| Class that replaces Gsutil to use a local directory instead of an online
|
| @@ -454,4 +510,4 @@ class DummyGsutil(download_from_google_storage.Gsutil):
|
|
|
|
|
| if __name__ == '__main__':
|
| - sys.exit(Main())
|
| + sys.exit(main(sys.argv[1:]))
|
|
|