|  | #!/usr/bin/env python | 
|  | # | 
|  | # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file | 
|  | # for details. All rights reserved. Use of this source code is governed by a | 
|  | # BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | # Gets or updates a content shell (a nearly headless build of chrome). This is | 
|  | # used for running browser tests of client applications. | 
|  |  | 
|  | import json | 
|  | import optparse | 
|  | import os | 
|  | import platform | 
|  | import shutil | 
|  | import subprocess | 
|  | import sys | 
|  | import tempfile | 
|  | import zipfile | 
|  |  | 
|  | import utils | 
|  |  | 
|  | def NormJoin(path1, path2): | 
|  | return os.path.normpath(os.path.join(path1, path2)) | 
|  |  | 
|  | # Change into the dart directory as we want the project to be rooted here. | 
|  | dart_src = NormJoin(os.path.dirname(sys.argv[0]), os.pardir) | 
|  | os.chdir(dart_src) | 
|  |  | 
|  | GSUTIL_DIR = os.path.join('third_party', 'gsutil') | 
|  | GSUTIL = GSUTIL_DIR + '/gsutil' | 
|  |  | 
|  | DRT_DIR = os.path.join('client', 'tests', 'drt') | 
|  | DRT_VERSION = os.path.join(DRT_DIR, 'LAST_VERSION') | 
|  | DRT_LATEST_PATTERN = ( | 
|  | 'gs://dartium-archive/latest/drt-%(osname)s-%(bot)s-*.zip') | 
|  | DRT_PERMANENT_PATTERN = ('gs://dartium-archive/drt-%(osname)s-%(bot)s/drt-' | 
|  | '%(osname)s-%(bot)s-%(num1)s.%(num2)s.zip') | 
|  |  | 
|  | DARTIUM_DIR = os.path.join('client', 'tests', 'dartium') | 
|  | DARTIUM_VERSION = os.path.join(DARTIUM_DIR, 'LAST_VERSION') | 
|  | DARTIUM_LATEST_PATTERN = ( | 
|  | 'gs://dartium-archive/latest/dartium-%(osname)s-%(bot)s-*.zip') | 
|  | DARTIUM_PERMANENT_PATTERN = ('gs://dartium-archive/dartium-%(osname)s-%(bot)s/' | 
|  | 'dartium-%(osname)s-%(bot)s-%(num1)s.%(num2)s.zip') | 
|  |  | 
|  | SDK_DIR = os.path.join(utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32'), | 
|  | 'dart-sdk') | 
|  | SDK_VERSION = os.path.join(SDK_DIR, 'LAST_VERSION') | 
|  | SDK_LATEST_PATTERN = 'gs://dart-archive/channels/dev/raw/latest/VERSION' | 
|  | # TODO(efortuna): Once the x64 VM also is optimized, select the version | 
|  | # based on whether we are running on a 32-bit or 64-bit system. | 
|  | SDK_PERMANENT = ('gs://dart-archive/channels/dev/raw/%(version_num)s/sdk/' + | 
|  | 'dartsdk-%(osname)s-ia32-release.zip') | 
|  |  | 
|  | # Dictionary storing the earliest revision of each download we have stored. | 
|  | LAST_VALID = {'dartium': 4285, 'chromedriver': 7823, 'sdk': 9761, 'drt': 5342} | 
|  |  | 
|  | sys.path.append(os.path.join(GSUTIL_DIR, 'third_party', 'boto')) | 
|  | import boto | 
|  |  | 
|  |  | 
|  | def ExecuteCommand(*cmd): | 
|  | """Execute a command in a subprocess.""" | 
|  | pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 
|  | output, error = pipe.communicate() | 
|  | return pipe.returncode, output | 
|  |  | 
|  |  | 
|  | def ExecuteCommandVisible(*cmd): | 
|  | """Execute a command in a subprocess, but show stdout/stderr.""" | 
|  | result = subprocess.call(cmd, stdout=sys.stdout, stderr=sys.stderr, | 
|  | stdin=sys.stdin) | 
|  | if result != 0: | 
|  | raise Exception('Execution of "%s" failed' % ' '.join(cmd)) | 
|  |  | 
|  |  | 
|  | def Gsutil(*cmd): | 
|  | return ExecuteCommand('python', GSUTIL, *cmd) | 
|  |  | 
|  |  | 
|  | def GsutilVisible(*cmd): | 
|  | ExecuteCommandVisible('python', GSUTIL, *cmd) | 
|  |  | 
|  |  | 
|  | def HasBotoConfig(): | 
|  | """Returns true if boto config exists.""" | 
|  |  | 
|  | config_paths = boto.pyami.config.BotoConfigLocations | 
|  | if 'AWS_CREDENTIAL_FILE' in os.environ: | 
|  | config_paths.append(os.environ['AWS_CREDENTIAL_FILE']) | 
|  | for config_path in config_paths: | 
|  | if os.path.exists(config_path): | 
|  | return True | 
|  |  | 
|  | return False | 
|  |  | 
|  |  | 
|  | def InRunhooks(): | 
|  | """True if this script was called by "gclient runhooks" or "gclient sync\"""" | 
|  | return 'runhooks' in sys.argv | 
|  |  | 
|  |  | 
|  | def GetDartiumRevision(name, bot, directory, version_file, latest_pattern, | 
|  | permanent_prefix, revision_num=None): | 
|  | """Get the latest binary that is stored in the dartium archive. | 
|  |  | 
|  | Args: | 
|  | name: the name of the desired download. | 
|  | directory: target directory (recreated) to install binary | 
|  | version_file: name of file with the current version stamp | 
|  | latest_pattern: the google store url pattern pointing to the latest binary | 
|  | permanent_prefix: stable google store folder used to download versions | 
|  | revision_num: The desired revision number to retrieve. If revision_num is | 
|  | None, we return the latest revision. If the revision number is specified | 
|  | but unavailable, find the nearest older revision and use that instead. | 
|  | """ | 
|  | osdict = {'Darwin':'mac-x64', 'Linux':'linux-x64', 'Windows':'win-ia32'} | 
|  |  | 
|  | def FindPermanentUrl(out, osname, the_revision_num): | 
|  | output_lines = out.split() | 
|  | latest = output_lines[-1] | 
|  | if not the_revision_num: | 
|  | latest = (permanent_prefix[:permanent_prefix.rindex('/')] % { 'osname' : | 
|  | osname, 'bot' : bot } + latest[latest.rindex('/'):]) | 
|  | else: | 
|  | latest = (permanent_prefix % { 'osname' : osname, 'num1' : the_revision_num, | 
|  | 'num2' : the_revision_num, 'bot' : bot }) | 
|  | foundURL = False | 
|  | while not foundURL: | 
|  | # Test to ensure this URL exists because the dartium-archive builds can | 
|  | # have unusual numbering (a range of CL numbers) sometimes. | 
|  | result, out = Gsutil('ls', permanent_prefix % {'osname' : osname, | 
|  | 'num1': the_revision_num, 'num2': '*', 'bot': bot }) | 
|  | if result == 0: | 
|  | # First try to find one with the second number the same as the | 
|  | # requested number. | 
|  | latest = out.split()[0] | 
|  | # Now test that the permissions are correct so you can actually | 
|  | # download it. | 
|  | temp_dir = tempfile.mkdtemp() | 
|  | temp_zip = os.path.join(temp_dir, 'foo.zip') | 
|  | returncode, out = Gsutil('cp', latest, 'file://' + temp_zip) | 
|  | if returncode == 0: | 
|  | foundURL = True | 
|  | else: | 
|  | # Unable to download this item (most likely because something went | 
|  | # wrong on the upload and the permissions are bad). Keep looking for | 
|  | # a different URL. | 
|  | the_revision_num = int(the_revision_num) - 1 | 
|  | shutil.rmtree(temp_dir) | 
|  | else: | 
|  | # Now try to find one with a nearby CL num. | 
|  | the_revision_num = int(the_revision_num) - 1 | 
|  | if the_revision_num <= 0: | 
|  | TooEarlyError() | 
|  | return latest | 
|  |  | 
|  | GetFromGsutil(name, directory, version_file, latest_pattern, osdict, | 
|  | FindPermanentUrl, revision_num, bot) | 
|  |  | 
|  |  | 
|  | def GetSdkRevision(name, directory, version_file, latest_pattern, | 
|  | permanent_prefix, revision_num): | 
|  | """Get a revision of the SDK from the editor build archive. | 
|  |  | 
|  | Args: | 
|  | name: the name of the desired download | 
|  | directory: target directory (recreated) to install binary | 
|  | version_file: name of file with the current version stamp | 
|  | latest_pattern: the google store url pattern pointing to the latest binary | 
|  | permanent_prefix: stable google store folder used to download versions | 
|  | revision_num: the desired revision number, or None for the most recent | 
|  | """ | 
|  | osdict = {'Darwin':'macos', 'Linux':'linux', 'Windows':'win32'} | 
|  | def FindPermanentUrl(out, osname, not_used): | 
|  | rev_num = revision_num | 
|  | if not rev_num: | 
|  | temp_file = tempfile.NamedTemporaryFile(delete=False) | 
|  | temp_file.close() | 
|  | temp_file_url = 'file://' + temp_file.name | 
|  | Gsutil('cp', latest_pattern % {'osname' : osname }, temp_file_url) | 
|  | temp_file = open(temp_file.name) | 
|  | temp_file.seek(0) | 
|  | version_info = temp_file.read() | 
|  | temp_file.close() | 
|  | os.unlink(temp_file.name) | 
|  | if version_info != '': | 
|  | rev_num = json.loads(version_info)['revision'] | 
|  | else: | 
|  | print 'Unable to get latest version information.' | 
|  | return '' | 
|  | latest = (permanent_prefix % { 'osname' : osname, 'version_num': rev_num}) | 
|  | return latest | 
|  |  | 
|  | GetFromGsutil(name, directory, version_file, latest_pattern, osdict, | 
|  | FindPermanentUrl, revision_num) | 
|  |  | 
|  |  | 
|  | def GetFromGsutil(name, directory, version_file, latest_pattern, | 
|  | os_name_dict, get_permanent_url, revision_num = '', bot = None): | 
|  | """Download and unzip the desired file from Google Storage. | 
|  | Args: | 
|  | name: the name of the desired download | 
|  | directory: target directory (recreated) to install binary | 
|  | version_file: name of file with the current version stamp | 
|  | latest_pattern: the google store url pattern pointing to the latest binary | 
|  | os_name_dict: a dictionary of operating system names and their corresponding | 
|  | strings on the google storage site. | 
|  | get_permanent_url: a function that accepts a listing of available files | 
|  | and the os name, and returns a permanent URL for downloading. | 
|  | revision_num: the desired revision number to get (if not supplied, we get | 
|  | the latest revision) | 
|  | """ | 
|  | system = platform.system() | 
|  | try: | 
|  | osname = os_name_dict[system] | 
|  | except KeyError: | 
|  | print >>sys.stderr, ('WARNING: platform "%s" does not support' | 
|  | '%s.') % (system, name) | 
|  | return 0 | 
|  |  | 
|  | # Query for the latest version | 
|  | pattern = latest_pattern  % { 'osname' : osname, 'bot' : bot } | 
|  | result, out = Gsutil('ls', pattern) | 
|  | if result == 0: | 
|  | # use permanent link instead, just in case the latest zip entry gets deleted | 
|  | # while we are downloading it. | 
|  | latest = get_permanent_url(out, osname, revision_num) | 
|  | else: # e.g. no access | 
|  | print "Couldn't download %s: %s\n%s" % (name, pattern, out) | 
|  | if not os.path.exists(version_file): | 
|  | print "Using %s will not work. Please try again later." % name | 
|  | return 0 | 
|  |  | 
|  | # Check if we need to update the file | 
|  | if os.path.exists(version_file): | 
|  | v = open(version_file, 'r').read() | 
|  | if v == latest: | 
|  | if not InRunhooks(): | 
|  | print name + ' is up to date.\nVersion: ' + latest | 
|  | return 0 # up to date | 
|  |  | 
|  | # download the zip file to a temporary path, and unzip to the target location | 
|  | temp_dir = tempfile.mkdtemp() | 
|  | try: | 
|  | temp_zip = os.path.join(temp_dir, 'drt.zip') | 
|  | temp_zip_url = 'file://' + temp_zip | 
|  | # It's nice to show download progress | 
|  | GsutilVisible('cp', latest, temp_zip_url) | 
|  |  | 
|  | if platform.system() != 'Windows': | 
|  | # The Python zip utility does not preserve executable permissions, but | 
|  | # this does not seem to be a problem for Windows, which does not have a | 
|  | # built in zip utility. :-/ | 
|  | result, out = ExecuteCommand('unzip', temp_zip, '-d', temp_dir) | 
|  | if result != 0: | 
|  | raise Exception('Execution of "unzip %s -d %s" failed: %s' % | 
|  | (temp_zip, temp_dir, str(out))) | 
|  | unzipped_dir = temp_dir + '/' + os.path.basename(latest)[:-len('.zip')] | 
|  | else: | 
|  | z = zipfile.ZipFile(temp_zip) | 
|  | z.extractall(temp_dir) | 
|  | unzipped_dir = os.path.join(temp_dir, | 
|  | os.path.basename(latest)[:-len('.zip')]) | 
|  | z.close() | 
|  | if directory == SDK_DIR: | 
|  | unzipped_dir = os.path.join(temp_dir, 'dart-sdk') | 
|  | if os.path.exists(directory): | 
|  | print 'Removing old %s tree %s' % (name, directory) | 
|  | shutil.rmtree(directory) | 
|  | if os.path.exists(directory): | 
|  | raise Exception( | 
|  | 'Removal of directory %s failed. Is the executable running?' % | 
|  | directory) | 
|  | shutil.move(unzipped_dir, directory) | 
|  | finally: | 
|  | shutil.rmtree(temp_dir) | 
|  |  | 
|  | # create the version stamp | 
|  | v = open(version_file, 'w') | 
|  | v.write(latest) | 
|  | v.close() | 
|  |  | 
|  | print 'Successfully downloaded to %s' % directory | 
|  | return 0 | 
|  |  | 
|  |  | 
|  | def TooEarlyError(): | 
|  | """Quick shortcutting function, to return early if someone requests a revision | 
|  | that is smaller than the earliest stored. This saves us from doing repeated | 
|  | requests until we get down to 0.""" | 
|  | print ('Unable to download requested revision because it is earlier than the ' | 
|  | 'earliest revision stored.') | 
|  | sys.exit(1) | 
|  |  | 
|  |  | 
|  | def CopyDrtFont(drt_dir): | 
|  | if platform.system() != 'Windows': | 
|  | return | 
|  | shutil.copy('third_party/drt_resources/AHEM____.TTF', drt_dir) | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parser = optparse.OptionParser(usage='usage: %prog [options] download_name') | 
|  | parser.add_option('-r', '--revision', dest='revision', | 
|  | help='Desired revision number to retrieve for the SDK. If ' | 
|  | 'unspecified, retrieve the latest SDK build.', | 
|  | action='store', default=None) | 
|  | parser.add_option('-d', '--debug', dest='debug', | 
|  | help='Download a debug archive instead of a release.', | 
|  | action='store_true', default=False) | 
|  | args, positional = parser.parse_args() | 
|  |  | 
|  | if args.revision and int(args.revision) < LAST_VALID[positional[0]]: | 
|  | return TooEarlyError() | 
|  |  | 
|  | # Use the incremental release bot ('dartium-*-inc-be') by default. | 
|  | # Issue 13399 Quick fix, update with channel support. | 
|  | bot = 'inc-be' | 
|  | if args.debug: | 
|  | print >>sys.stderr, ( | 
|  | 'Debug versions of Dartium and content_shell not available') | 
|  | return 1 | 
|  |  | 
|  | if positional[0] == 'dartium': | 
|  | GetDartiumRevision('Dartium', bot, DARTIUM_DIR, DARTIUM_VERSION, | 
|  | DARTIUM_LATEST_PATTERN, DARTIUM_PERMANENT_PATTERN, | 
|  | args.revision) | 
|  | elif positional[0] == 'sdk': | 
|  | GetSdkRevision('sdk', SDK_DIR, SDK_VERSION, SDK_LATEST_PATTERN, | 
|  | SDK_PERMANENT, args.revision) | 
|  | elif positional[0] == 'drt': | 
|  | GetDartiumRevision('content_shell', bot, DRT_DIR, DRT_VERSION, | 
|  | DRT_LATEST_PATTERN, DRT_PERMANENT_PATTERN, | 
|  | args.revision) | 
|  | CopyDrtFont(DRT_DIR) | 
|  | else: | 
|  | print ('Please specify the target you wish to download from Google Storage ' | 
|  | '("drt", "dartium", "chromedriver", or "sdk")') | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |