1#!/usr/bin/env python2
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.
5
6"""Fetch prebuilt binaries to run PyAuto.
7
8Sets up Chrome and PyAuto binaries using prebuilt binaries from the
9continuous build archives.  Works on mac, win, linux (32 & 64 bit).
10
11Examples:
12  On Mac:
13  $ python fetch_prebuilt_pyauto.py -d xcodebuild/Release
14      http://build.chromium.org/f/chromium/continuous/mac/LATEST
15
16  On Win:
17  $ python fetch_prebuilt_pyauto.py -d chrome\Release
18      http://build.chromium.org/f/chromium/continuous/win/LATEST
19"""
20
21from __future__ import print_function
22
23import glob
24import httplib
25import optparse
26import os
27import platform
28import shutil
29import sys
30import urllib
31import urllib2
32import urlparse
33
34import pyauto_utils
35
36
37class FetchPrebuilt(object):
38  """Util class to fetch prebuilt binaries to run PyAuto."""
39
40  def _ParseArgs(self):
41    parser = optparse.OptionParser()
42    parser.add_option(
43        '-d', '--outdir', type='string', default=None,
44        help='Directory in which to setup. This is typically the directory '
45        'where the binaries would go when compiled from source.')
46    parser.add_option(
47        '-p', '--platform', type='string',
48        default=pyauto_utils.GetCurrentPlatform(),
49        help='Platform. Valid options: win, mac, linux32, linux64. '
50        'Default: current platform (%s)' % pyauto_utils.GetCurrentPlatform())
51    parser.add_option(
52        '-l', '--latest', action='store_true', default=False,
53        help='Download the latest chromium build from commondatastorage. '
54        '[default=False]')
55    self._options, self._args = parser.parse_args()
56    if self._options.latest:
57      self._url = self._GetLastestDownloadURL(self._options.platform)
58    elif not self._args:
59      print >>sys.stderr, 'Need download url'
60      sys.exit(2)
61    else:
62      self._url = self._args[0]
63    if not self._options.outdir:
64      print >>sys.stderr, 'Need output directory: -d/--outdir'
65      sys.exit(1)
66    self._outdir = self._options.outdir
67    # Chromium continuous build archive has a non-standard format.
68    if 'index.html?path=' in self._url:
69      self._url = self._url.replace('index.html?path=', '')
70    self._url = self._url.rstrip('/')
71    # Determine name of zip.
72    if not self._options.platform.startswith('linux'):
73      self._chrome_zip_name = 'chrome-%s' % {'mac': 'mac',
74                                             'win': 'win32'
75                                            }[self._options.platform]
76    else:
77      linux_32_names = ['linux', 'lucid32bit']
78      linux_64_names = ['linux64', 'lucid64bit']
79      linux_names = {'linux': linux_32_names + linux_64_names,
80                     'linux32': linux_32_names,
81                     'linux64': linux_64_names
82                    }[self._options.platform]
83      for name in linux_names:
84        zip_name = 'chrome-' + name
85        if pyauto_utils.DoesUrlExist('%s/%s.zip' % (self._url, zip_name)):
86          self._chrome_zip_name = zip_name
87          break
88      else:
89        raise RuntimeError('Could not find chrome zip at ' + self._url)
90
91    # Setup urls to download.
92    self._chrome_zip_url = '%s/%s.zip' % (self._url, self._chrome_zip_name)
93    self._remoting_zip_url = self._url + '/' + 'remoting-webapp.zip'
94    chrome_test_url = '%s/%s.test' % (self._url, self._chrome_zip_name)
95    self._pyautolib_py_url = '%s/pyautolib.py' % chrome_test_url
96    if self._options.platform == 'win':
97      self._pyautolib_so_name = '_pyautolib.pyd'
98      self._chromedriver_name = 'chromedriver.exe'
99    else:
100      self._pyautolib_so_name = '_pyautolib.so'
101      self._chromedriver_name = 'chromedriver'
102    if self._options.platform == 'mac':
103      self._ffmpegsumo_so_name = 'ffmpegsumo.so'
104      self._ffmpegsumo_so_url = chrome_test_url + '/' + self._ffmpegsumo_so_name
105    self._pyautolib_so_url = chrome_test_url + '/' + self._pyautolib_so_name
106    self._chromedriver_url = chrome_test_url + '/' + self._chromedriver_name
107
108  def _GetLastestDownloadURL(self, os_platform):
109    os_type = {'win': 'Win',
110               'mac': 'Mac',
111               'linux': 'Linux',
112               'linux32': 'Linux',
113               'linux64': 'Linux_x64'}[os_platform]
114    if os_type == 'Linux' and platform.architecture()[0] == '64bit':
115      os_type = 'Linux_x64'
116    last_change_url = ('http://commondatastorage.googleapis.com/'
117                       'chromium-browser-continuous/%s/LAST_CHANGE' % os_type)
118    response = urllib2.urlopen(last_change_url)
119    last_change = response.read()
120    if not last_change:
121      print >>sys.stderr, ('Unable to get latest from %s' % last_change_url)
122      sys.exit(2)
123    last_change_url = ('http://commondatastorage.googleapis.com/'
124                       'chromium-browser-continuous/%s/%s' % (os_type,
125                                                              last_change))
126    return last_change_url
127
128  def Cleanup(self):
129    """Remove old binaries, if any."""
130    pass
131
132  def Run(self):
133    self._ParseArgs()
134    if not os.path.isdir(self._outdir):
135      os.makedirs(self._outdir)
136    get_remoting = pyauto_utils.DoesUrlExist(self._remoting_zip_url)
137
138    # Fetch chrome & pyauto binaries
139    print('Fetching', self._chrome_zip_url)
140    chrome_zip = urllib.urlretrieve(self._chrome_zip_url)[0]
141
142    if get_remoting:
143      print('Fetching', self._remoting_zip_url)
144      remoting_zip = urllib.urlretrieve(self._remoting_zip_url)[0]
145    else:
146      print('Warning: %s does not exist.' % self._remoting_zip_url)
147
148    print('Fetching', self._pyautolib_py_url)
149    pyautolib_py = urllib.urlretrieve(self._pyautolib_py_url)[0]
150
151    print('Fetching', self._pyautolib_so_url)
152    pyautolib_so = urllib.urlretrieve(self._pyautolib_so_url)[0]
153
154    if self._options.platform == 'mac':
155      print('Fetching', self._ffmpegsumo_so_url)
156      ffmpegsumo_so = urllib.urlretrieve(self._ffmpegsumo_so_url)[0]
157
158    print('Fetching', self._chromedriver_url)
159    chromedriver = urllib.urlretrieve(self._chromedriver_url)[0]
160
161    chrome_unzip_dir = os.path.join(self._outdir, self._chrome_zip_name)
162    if os.path.exists(chrome_unzip_dir):
163      print('Cleaning', chrome_unzip_dir)
164      pyauto_utils.RemovePath(chrome_unzip_dir)
165    print('Unzipping')
166    pyauto_utils.UnzipFilenameToDir(chrome_zip, self._outdir)
167    if get_remoting:
168      pyauto_utils.UnzipFilenameToDir(remoting_zip, self._outdir)
169      shutil.move(self._outdir + '/remoting-webapp',
170                  self._outdir + '/remoting/remoting.webapp')
171
172    # Copy over the binaries to outdir
173    items_to_copy = {
174      pyautolib_py: os.path.join(self._outdir, 'pyautolib.py'),
175      pyautolib_so: os.path.join(self._outdir, self._pyautolib_so_name),
176      chromedriver: os.path.join(self._outdir, self._chromedriver_name)
177    }
178    if self._options.platform == 'mac':
179      items_to_copy[ffmpegsumo_so] = \
180          os.path.join(self._outdir, self._ffmpegsumo_so_name)
181
182    unzip_dir_contents = glob.glob(os.path.join(chrome_unzip_dir, '*'))
183    for item in unzip_dir_contents:
184      name = os.path.basename(item)
185      items_to_copy[item] = os.path.join(self._outdir, name)
186
187    for src, dest in items_to_copy.iteritems():
188      pyauto_utils.RemovePath(dest)
189      print('%s ==> %s' % (os.path.basename(src), dest))
190      shutil.move(src, dest)
191    pyauto_utils.RemovePath(chrome_unzip_dir)
192
193    # Final setup (if any)
194    # Set executable bit on chromedriver binary.
195    if not self._options.platform == 'win':
196      os.chmod(items_to_copy[chromedriver], 0o0700)
197
198    # Create symlink to .framework on Mac
199    if self._options.platform == 'mac':
200      mac_app_name = os.path.basename([x for x in unzip_dir_contents
201                                       if x.endswith('.app')][0])
202      os.chdir(self._outdir)
203      framework = glob.glob(os.path.join(
204          mac_app_name, 'Contents', 'Versions', '*', '*.framework'))[0]
205      print(framework)
206      dest = os.path.basename(framework)
207      os.path.lexists(dest) and os.remove(dest)
208      print('Creating symlink "%s"' % dest)
209      os.symlink(framework, dest)
210
211    print('Prepared binaries in "%s"' % self._outdir)
212    return 0
213
214
215if __name__ == '__main__':
216  sys.exit(FetchPrebuilt().Run())
217