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