build-webapp.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
1#!/usr/bin/env python
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"""Creates a directory with with the unpacked contents of the remoting webapp.
7
8The directory will contain a copy-of or a link-to to all remoting webapp
9resources.  This includes HTML/JS and any plugin binaries. The script also
10massages resulting files appropriately with host plugin data. Finally,
11a zip archive for all of the above is produced.
12"""
13
14# Python 2.5 compatibility
15from __future__ import with_statement
16
17import os
18import platform
19import re
20import shutil
21import subprocess
22import sys
23import time
24import zipfile
25
26# Update the module path, assuming that this script is in src/remoting/webapp,
27# and that the google_api_keys module is in src/google_apis. Note that
28# sys.path[0] refers to the directory containing this script.
29if __name__ == '__main__':
30  sys.path.append(
31      os.path.abspath(os.path.join(sys.path[0], '../../google_apis')))
32import google_api_keys
33
34def findAndReplace(filepath, findString, replaceString):
35  """Does a search and replace on the contents of a file."""
36  oldFilename = os.path.basename(filepath) + '.old'
37  oldFilepath = os.path.join(os.path.dirname(filepath), oldFilename)
38  os.rename(filepath, oldFilepath)
39  with open(oldFilepath) as input:
40    with open(filepath, 'w') as output:
41      for s in input:
42        output.write(s.replace(findString, replaceString))
43  os.remove(oldFilepath)
44
45
46def createZip(zip_path, directory):
47  """Creates a zipfile at zip_path for the given directory."""
48  zipfile_base = os.path.splitext(os.path.basename(zip_path))[0]
49  zip = zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED)
50  for (root, dirs, files) in os.walk(directory):
51    for f in files:
52      full_path = os.path.join(root, f)
53      rel_path = os.path.relpath(full_path, directory)
54      zip.write(full_path, os.path.join(zipfile_base, rel_path))
55  zip.close()
56
57
58def buildWebApp(buildtype, version, mimetype, destination, zip_path, plugin,
59                files, locales):
60  """Does the main work of building the webapp directory and zipfile.
61
62  Args:
63    buildtype: the type of build ("Official" or "Dev")
64    mimetype: A string with mimetype of plugin.
65    destination: A string with path to directory where the webapp will be
66                 written.
67    zipfile: A string with path to the zipfile to create containing the
68             contents of |destination|.
69    plugin: A string with path to the binary plugin for this webapp.
70    files: An array of strings listing the paths for resources to include
71           in this webapp.
72    locales: An array of strings listing locales, which are copied, along
73             with their directory structure from the _locales directory down.
74  """
75  # Ensure a fresh directory.
76  try:
77    shutil.rmtree(destination)
78  except OSError:
79    if os.path.exists(destination):
80      raise
81    else:
82      pass
83  os.mkdir(destination, 0775)
84
85  # Use symlinks on linux and mac for faster compile/edit cycle.
86  #
87  # On Windows Vista platform.system() can return 'Microsoft' with some
88  # versions of Python, see http://bugs.python.org/issue1082
89  # should_symlink = platform.system() not in ['Windows', 'Microsoft']
90  #
91  # TODO(ajwong): Pending decision on http://crbug.com/27185 we may not be
92  # able to load symlinked resources.
93  should_symlink = False
94
95  # Copy all the files.
96  for current_file in files:
97    destination_file = os.path.join(destination, os.path.basename(current_file))
98    destination_dir = os.path.dirname(destination_file)
99    if not os.path.exists(destination_dir):
100      os.makedirs(destination_dir, 0775)
101
102    if should_symlink:
103      # TODO(ajwong): Detect if we're vista or higher.  Then use win32file
104      # to create a symlink in that case.
105      targetname = os.path.relpath(os.path.realpath(current_file),
106                                   os.path.realpath(destination_file))
107      os.symlink(targetname, destination_file)
108    else:
109      shutil.copy2(current_file, destination_file)
110
111  # Copy all the locales, preserving directory structure
112  destination_locales = os.path.join(destination, "_locales")
113  os.mkdir(destination_locales , 0775)
114  locale_dir = "/_locales/"
115  for current_locale in locales:
116    pos = current_locale.find(locale_dir)
117    if (pos == -1):
118      raise Exception("Missing locales directory in " + current_locale)
119    subtree = current_locale[pos + len(locale_dir):]
120    pos = subtree.find("/")
121    if (pos == -1):
122      raise Exception("Malformed locale: " + current_locale)
123    locale_id = subtree[:pos]
124    messages = subtree[pos+1:]
125    destination_dir = os.path.join(destination_locales, locale_id)
126    destination_file = os.path.join(destination_dir, messages)
127    os.mkdir(destination_dir, 0775)
128    shutil.copy2(current_locale, destination_file)
129
130  # Create fake plugin files to appease the manifest checker.
131  # It requires that if there is a plugin listed in the manifest that
132  # there be a file in the plugin with that name.
133  names = [
134    'remoting_host_plugin.dll',  # Windows
135    'remoting_host_plugin.plugin',  # Mac
136    'libremoting_host_plugin.ia32.so',  # Linux 32
137    'libremoting_host_plugin.x64.so'  # Linux 64
138  ]
139  pluginName = os.path.basename(plugin)
140
141  for name in names:
142    if name != pluginName:
143      path = os.path.join(destination, name)
144      f = open(path, 'w')
145      f.write("placeholder for %s" % (name))
146      f.close()
147
148  # Copy the plugin.
149  pluginName = os.path.basename(plugin)
150  newPluginPath = os.path.join(destination, pluginName)
151  if os.path.isdir(plugin):
152    # On Mac we have a directory.
153    shutil.copytree(plugin, newPluginPath)
154  else:
155    shutil.copy2(plugin, newPluginPath)
156
157  # Strip the linux build.
158  if ((platform.system() == 'Linux') and (buildtype == 'Official')):
159    subprocess.call(["strip", newPluginPath])
160
161  # Set the version number in the manifest version.
162  findAndReplace(os.path.join(destination, 'manifest.json'),
163                 'FULL_APP_VERSION',
164                 version)
165
166  # Set the correct mimetype.
167  findAndReplace(os.path.join(destination, 'plugin_settings.js'),
168                 'HOST_PLUGIN_MIMETYPE',
169                 mimetype)
170
171  # Set the correct OAuth2 redirect URL.
172  baseUrl = (
173      'https://chromoting-oauth.talkgadget.google.com/'
174      'talkgadget/oauth/chrome-remote-desktop')
175  if (buildtype == 'Official'):
176    oauth2RedirectUrlJs = (
177        "'" + baseUrl + "/rel/' + chrome.i18n.getMessage('@@extension_id')")
178    oauth2RedirectUrlJson = baseUrl + '/rel/*'
179  else:
180    oauth2RedirectUrlJs = "'" + baseUrl + "/dev'"
181    oauth2RedirectUrlJson = baseUrl + '/dev*'
182  findAndReplace(os.path.join(destination, 'plugin_settings.js'),
183                 "'OAUTH2_REDIRECT_URL'",
184                 oauth2RedirectUrlJs)
185  findAndReplace(os.path.join(destination, 'manifest.json'),
186                 "OAUTH2_REDIRECT_URL",
187                 oauth2RedirectUrlJson)
188
189  # Set the correct API keys.
190  apiClientId = google_api_keys.GetClientID('REMOTING')
191  apiClientSecret = google_api_keys.GetClientSecret('REMOTING')
192
193  findAndReplace(os.path.join(destination, 'plugin_settings.js'),
194                 "'API_CLIENT_ID'",
195                 "'" + apiClientId + "'")
196  findAndReplace(os.path.join(destination, 'plugin_settings.js'),
197                 "'API_CLIENT_SECRET'",
198                 "'" + apiClientSecret + "'")
199
200  # Make the zipfile.
201  createZip(zip_path, destination)
202
203
204def main():
205  if len(sys.argv) < 7:
206    print ('Usage: build-webapp.py '
207           '<build-type> <version> <mime-type> <dst> <zip-path> <plugin> '
208           '<other files...> --locales <locales...>')
209    return 1
210
211  reading_locales = False
212  files = []
213  locales = []
214  for arg in sys.argv[7:]:
215    if arg == "--locales":
216      reading_locales = True;
217    elif reading_locales:
218      locales.append(arg)
219    else:
220      files.append(arg)
221
222  buildWebApp(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5],
223              sys.argv[6], files, locales)
224  return 0
225
226
227if __name__ == '__main__':
228  sys.exit(main())
229