Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Creates a directory with with the unpacked contents of the remoting webapp. | 6 """Creates a directory with with the unpacked contents of the remoting webapp. |
| 7 | 7 |
| 8 The directory will contain a copy-of or a link-to to all remoting webapp | 8 The directory will contain a copy-of or a link-to to all remoting webapp |
| 9 resources. This includes HTML/JS and any plugin binaries. The script also | 9 resources. This includes HTML/JS and any plugin binaries. The script also |
| 10 massages resulting files appropriately with host plugin data. Finally, | 10 massages resulting files appropriately with host plugin data. Finally, |
| 11 a zip archive for all of the above is produced. | 11 a zip archive for all of the above is produced. |
| 12 """ | 12 """ |
| 13 | 13 |
| 14 # Python 2.5 compatibility | 14 # Python 2.5 compatibility |
| 15 from __future__ import with_statement | 15 from __future__ import with_statement |
| 16 | 16 |
| 17 import argparse | |
| 17 import io | 18 import io |
| 18 import os | 19 import os |
| 19 import platform | 20 import platform |
| 20 import re | 21 import re |
| 21 import shutil | 22 import shutil |
| 22 import subprocess | 23 import subprocess |
| 23 import sys | 24 import sys |
| 24 import time | 25 import time |
| 25 import zipfile | 26 import zipfile |
| 26 | 27 |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 55 rel_path = os.path.relpath(full_path, directory) | 56 rel_path = os.path.relpath(full_path, directory) |
| 56 zip.write(full_path, os.path.join(zipfile_base, rel_path)) | 57 zip.write(full_path, os.path.join(zipfile_base, rel_path)) |
| 57 zip.close() | 58 zip.close() |
| 58 | 59 |
| 59 | 60 |
| 60 def replaceString(destination, placeholder, value): | 61 def replaceString(destination, placeholder, value): |
| 61 findAndReplace(os.path.join(destination, 'plugin_settings.js'), | 62 findAndReplace(os.path.join(destination, 'plugin_settings.js'), |
| 62 "'" + placeholder + "'", "'" + value + "'") | 63 "'" + placeholder + "'", "'" + value + "'") |
| 63 | 64 |
| 64 | 65 |
| 66 def replaceBool(destination, placeholder, value): | |
| 67 findAndReplace(os.path.join(destination, 'plugin_settings.js'), | |
| 68 "!!'" + placeholder + "'", 'true' if value else 'false') | |
|
Sergey Ulanov
2015/03/25 18:56:43
Why do you need !! here?
John Williams
2015/03/25 20:32:19
The JS code is typechecked before being processed
Sergey Ulanov
2015/03/25 20:50:50
Right, but type is explicitly specified there anyw
John Williams
2015/03/25 22:54:51
Done.
| |
| 69 | |
| 70 | |
| 65 def processJinjaTemplate(input_file, include_paths, output_file, context): | 71 def processJinjaTemplate(input_file, include_paths, output_file, context): |
| 66 jinja2_path = os.path.normpath( | 72 jinja2_path = os.path.normpath( |
| 67 os.path.join(os.path.abspath(__file__), | 73 os.path.join(os.path.abspath(__file__), |
| 68 '../../../third_party/jinja2')) | 74 '../../../third_party/jinja2')) |
| 69 sys.path.append(os.path.split(jinja2_path)[0]) | 75 sys.path.append(os.path.split(jinja2_path)[0]) |
| 70 import jinja2 | 76 import jinja2 |
| 71 (template_path, template_name) = os.path.split(input_file) | 77 (template_path, template_name) = os.path.split(input_file) |
| 72 include_paths = [template_path] + include_paths | 78 include_paths = [template_path] + include_paths |
| 73 env = jinja2.Environment(loader=jinja2.FileSystemLoader(include_paths)) | 79 env = jinja2.Environment(loader=jinja2.FileSystemLoader(include_paths)) |
| 74 template = env.get_template(template_name) | 80 template = env.get_template(template_name) |
| 75 rendered = template.render(context) | 81 rendered = template.render(context) |
| 76 io.open(output_file, 'w', encoding='utf-8').write(rendered) | 82 io.open(output_file, 'w', encoding='utf-8').write(rendered) |
| 77 | 83 |
| 78 def buildWebApp(buildtype, version, destination, zip_path, | 84 def buildWebApp(buildtype, version, destination, zip_path, |
| 79 manifest_template, webapp_type, app_id, app_name, | 85 manifest_template, webapp_type, appid, app_name, |
| 80 app_description, app_capabilities, files, locales, jinja_paths, | 86 app_description, app_capabilities, files, locales_listfile, |
| 81 service_environment): | 87 jinja_paths, service_environment, use_gcd): |
| 82 """Does the main work of building the webapp directory and zipfile. | 88 """Does the main work of building the webapp directory and zipfile. |
| 83 | 89 |
| 84 Args: | 90 Args: |
| 85 buildtype: the type of build ("Official", "Release" or "Dev"). | 91 buildtype: the type of build ("Official", "Release" or "Dev"). |
| 86 destination: A string with path to directory where the webapp will be | 92 destination: A string with path to directory where the webapp will be |
| 87 written. | 93 written. |
| 88 zipfile: A string with path to the zipfile to create containing the | 94 zipfile: A string with path to the zipfile to create containing the |
| 89 contents of |destination|. | 95 contents of |destination|. |
| 90 manifest_template: jinja2 template file for manifest. | 96 manifest_template: jinja2 template file for manifest. |
| 91 webapp_type: webapp type ("v1", "v2", "v2_pnacl" or "app_remoting"). | 97 webapp_type: webapp type ("v1", "v2", "v2_pnacl" or "app_remoting"). |
| 92 app_id: A string with the Remoting Application Id (only used for app | 98 appid: A string with the Remoting Application Id (only used for app |
| 93 remoting webapps). If supplied, it defaults to using the | 99 remoting webapps). If supplied, it defaults to using the |
| 94 test API server. | 100 test API server. |
| 95 app_name: A string with the name of the application. | 101 app_name: A string with the name of the application. |
| 96 app_description: A string with the description of the application. | 102 app_description: A string with the description of the application. |
| 97 app_capabilities: A set of strings naming the capabilities that should be | 103 app_capabilities: A set of strings naming the capabilities that should be |
| 98 enabled for this application. | 104 enabled for this application. |
| 99 files: An array of strings listing the paths for resources to include | 105 files: An array of strings listing the paths for resources to include |
| 100 in this webapp. | 106 in this webapp. |
| 101 locales: An array of strings listing locales, which are copied, along | 107 locales_listfile: The name of a file containing a list of locales, one per |
| 102 with their directory structure from the _locales directory down. | 108 line, which are copied, along with their directory structure, from |
| 109 the _locales directory down. | |
| 103 jinja_paths: An array of paths to search for {%include} directives in | 110 jinja_paths: An array of paths to search for {%include} directives in |
| 104 addition to the directory containing the manifest template. | 111 addition to the directory containing the manifest template. |
| 105 service_environment: Used to point the webApp to one of the | 112 service_environment: Used to point the webApp to one of the |
| 106 dev/test/staging/prod environments | 113 dev/test/staging/prod environments |
| 114 use_gcd: True if GCD support should be enabled. | |
| 107 """ | 115 """ |
| 116 | |
| 117 # Load the locales files from the locales_listfile. | |
| 118 if not locales_listfile: | |
| 119 raise Exception('You must specify a locales_listfile') | |
| 120 locales = [] | |
| 121 with open(locales_listfile) as input: | |
| 122 for s in input: | |
| 123 locales.append(s.rstrip()) | |
| 124 | |
| 108 # Ensure a fresh directory. | 125 # Ensure a fresh directory. |
| 109 try: | 126 try: |
| 110 shutil.rmtree(destination) | 127 shutil.rmtree(destination) |
| 111 except OSError: | 128 except OSError: |
| 112 if os.path.exists(destination): | 129 if os.path.exists(destination): |
| 113 raise | 130 raise |
| 114 else: | 131 else: |
| 115 pass | 132 pass |
| 116 os.mkdir(destination, 0775) | 133 os.mkdir(destination, 0775) |
| 117 | 134 |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 183 # apps for release (no impersonation) instead of dev. | 200 # apps for release (no impersonation) instead of dev. |
| 184 if service_environment == 'prod' and buildtype == 'Dev': | 201 if service_environment == 'prod' and buildtype == 'Dev': |
| 185 raise Exception("Prod environment cannot be built for 'dev' builds") | 202 raise Exception("Prod environment cannot be built for 'dev' builds") |
| 186 | 203 |
| 187 if buildtype != 'Dev': | 204 if buildtype != 'Dev': |
| 188 if service_environment != 'prod': | 205 if service_environment != 'prod': |
| 189 raise Exception('Invalid service_environment targeted for ' | 206 raise Exception('Invalid service_environment targeted for ' |
| 190 + buildtype + ': ' + service_environment) | 207 + buildtype + ': ' + service_environment) |
| 191 if 'out/Release' not in destination and 'out\Release' not in destination: | 208 if 'out/Release' not in destination and 'out\Release' not in destination: |
| 192 raise Exception('Prod builds must be placed in the out/Release folder') | 209 raise Exception('Prod builds must be placed in the out/Release folder') |
| 193 if app_id != None: | 210 if appid != None: |
| 194 raise Exception('Cannot pass in an app_id for ' | 211 raise Exception('Cannot pass in an appid for ' |
| 195 + buildtype + ' builds: ' + service_environment) | 212 + buildtype + ' builds: ' + service_environment) |
| 196 if appRemotingApiHost != None: | 213 if appRemotingApiHost != None: |
| 197 raise Exception('Cannot set APP_REMOTING_API_HOST env var for ' | 214 raise Exception('Cannot set APP_REMOTING_API_HOST env var for ' |
| 198 + buildtype + ' builds') | 215 + buildtype + ' builds') |
| 199 if appRemotingApplicationId != None: | 216 if appRemotingApplicationId != None: |
| 200 raise Exception('Cannot set APP_REMOTING_APPLICATION_ID env var for ' | 217 raise Exception('Cannot set APP_REMOTING_APPLICATION_ID env var for ' |
| 201 + buildtype + ' builds') | 218 + buildtype + ' builds') |
| 202 | 219 |
| 203 # If an Application ID was set (either from service_environment variable or | 220 # If an Application ID was set (either from service_environment variable or |
| 204 # from a command line argument), hardcode it, otherwise get it at runtime. | 221 # from a command line argument), hardcode it, otherwise get it at runtime. |
| 205 effectiveAppId = appRemotingApplicationId or app_id | 222 effectiveAppId = appRemotingApplicationId or appid |
| 206 if effectiveAppId: | 223 if effectiveAppId: |
| 207 appRemotingApplicationId = "'" + effectiveAppId + "'" | 224 appRemotingApplicationId = "'" + effectiveAppId + "'" |
| 208 else: | 225 else: |
| 209 appRemotingApplicationId = "chrome.i18n.getMessage('@@extension_id')" | 226 appRemotingApplicationId = "chrome.i18n.getMessage('@@extension_id')" |
| 210 findAndReplace(os.path.join(destination, 'plugin_settings.js'), | 227 findAndReplace(os.path.join(destination, 'plugin_settings.js'), |
| 211 "'APP_REMOTING_APPLICATION_ID'", appRemotingApplicationId) | 228 "'APP_REMOTING_APPLICATION_ID'", appRemotingApplicationId) |
| 212 | 229 |
| 213 oauth2BaseUrl = oauth2AccountsHost + '/o/oauth2' | 230 oauth2BaseUrl = oauth2AccountsHost + '/o/oauth2' |
| 214 oauth2ApiBaseUrl = oauth2ApiHost + '/oauth2' | 231 oauth2ApiBaseUrl = oauth2ApiHost + '/oauth2' |
| 215 directoryApiBaseUrl = directoryApiHost + '/chromoting/v1' | 232 directoryApiBaseUrl = directoryApiHost + '/chromoting/v1' |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 229 elif service_environment == 'staging': | 246 elif service_environment == 'staging': |
| 230 appRemotingServicePath = '/appremoting/v1beta1_staging' | 247 appRemotingServicePath = '/appremoting/v1beta1_staging' |
| 231 elif service_environment == 'prod': | 248 elif service_environment == 'prod': |
| 232 appRemotingServicePath = '/appremoting/v1beta1' | 249 appRemotingServicePath = '/appremoting/v1beta1' |
| 233 else: | 250 else: |
| 234 raise Exception('Unknown service environment: ' + service_environment) | 251 raise Exception('Unknown service environment: ' + service_environment) |
| 235 appRemotingApiBaseUrl = appRemotingApiHost + appRemotingServicePath | 252 appRemotingApiBaseUrl = appRemotingApiHost + appRemotingServicePath |
| 236 else: | 253 else: |
| 237 appRemotingApiBaseUrl = '' | 254 appRemotingApiBaseUrl = '' |
| 238 | 255 |
| 256 replaceBool(destination, 'USE_GCD', use_gcd) | |
| 239 replaceString(destination, 'OAUTH2_BASE_URL', oauth2BaseUrl) | 257 replaceString(destination, 'OAUTH2_BASE_URL', oauth2BaseUrl) |
| 240 replaceString(destination, 'OAUTH2_API_BASE_URL', oauth2ApiBaseUrl) | 258 replaceString(destination, 'OAUTH2_API_BASE_URL', oauth2ApiBaseUrl) |
| 241 replaceString(destination, 'DIRECTORY_API_BASE_URL', directoryApiBaseUrl) | 259 replaceString(destination, 'DIRECTORY_API_BASE_URL', directoryApiBaseUrl) |
| 242 if webapp_type == 'app_remoting': | 260 if webapp_type == 'app_remoting': |
| 243 replaceString(destination, 'APP_REMOTING_API_BASE_URL', | 261 replaceString(destination, 'APP_REMOTING_API_BASE_URL', |
| 244 appRemotingApiBaseUrl) | 262 appRemotingApiBaseUrl) |
| 245 | 263 |
| 246 # Substitute hosts in the manifest's CSP list. | 264 # Substitute hosts in the manifest's CSP list. |
| 247 # Ensure we list the API host only once if it's the same for multiple APIs. | 265 # Ensure we list the API host only once if it's the same for multiple APIs. |
| 248 googleApiHosts = ' '.join(set([oauth2ApiHost, directoryApiHost])) | 266 googleApiHosts = ' '.join(set([oauth2ApiHost, directoryApiHost])) |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 315 # Use a consistent extension id for dev builds. | 333 # Use a consistent extension id for dev builds. |
| 316 # AppRemoting builds always use the dev app id - the correct app id gets | 334 # AppRemoting builds always use the dev app id - the correct app id gets |
| 317 # written into the manifest later. | 335 # written into the manifest later. |
| 318 if buildtype != 'Official' or webapp_type == 'app_remoting': | 336 if buildtype != 'Official' or webapp_type == 'app_remoting': |
| 319 manifestKey = '"key": "remotingdevbuild",' | 337 manifestKey = '"key": "remotingdevbuild",' |
| 320 else: | 338 else: |
| 321 manifestKey = '' | 339 manifestKey = '' |
| 322 | 340 |
| 323 # Generate manifest. | 341 # Generate manifest. |
| 324 if manifest_template: | 342 if manifest_template: |
| 343 print 'args.use_gcd {!r}'.format(use_gcd) | |
|
Sergey Ulanov
2015/03/25 18:56:43
remove this?
John Williams
2015/03/25 20:32:19
Oops.
| |
| 325 context = { | 344 context = { |
| 326 'webapp_type': webapp_type, | 345 'webapp_type': webapp_type, |
| 327 'FULL_APP_VERSION': version, | 346 'FULL_APP_VERSION': version, |
| 328 'MANIFEST_KEY_FOR_UNOFFICIAL_BUILD': manifestKey, | 347 'MANIFEST_KEY_FOR_UNOFFICIAL_BUILD': manifestKey, |
| 329 'OAUTH2_REDIRECT_URL': oauth2RedirectUrlJson, | 348 'OAUTH2_REDIRECT_URL': oauth2RedirectUrlJson, |
| 330 'TALK_GADGET_HOST': talkGadgetHostJson, | 349 'TALK_GADGET_HOST': talkGadgetHostJson, |
| 331 'THIRD_PARTY_AUTH_REDIRECT_URL': thirdPartyAuthUrlJson, | 350 'THIRD_PARTY_AUTH_REDIRECT_URL': thirdPartyAuthUrlJson, |
| 332 'REMOTING_IDENTITY_API_CLIENT_ID': apiClientIdV2, | 351 'REMOTING_IDENTITY_API_CLIENT_ID': apiClientIdV2, |
| 333 'OAUTH2_BASE_URL': oauth2BaseUrl, | 352 'OAUTH2_BASE_URL': oauth2BaseUrl, |
| 334 'OAUTH2_API_BASE_URL': oauth2ApiBaseUrl, | 353 'OAUTH2_API_BASE_URL': oauth2ApiBaseUrl, |
| 335 'DIRECTORY_API_BASE_URL': directoryApiBaseUrl, | 354 'DIRECTORY_API_BASE_URL': directoryApiBaseUrl, |
| 336 'APP_REMOTING_API_BASE_URL': appRemotingApiBaseUrl, | 355 'APP_REMOTING_API_BASE_URL': appRemotingApiBaseUrl, |
| 337 'OAUTH2_ACCOUNTS_HOST': oauth2AccountsHost, | 356 'OAUTH2_ACCOUNTS_HOST': oauth2AccountsHost, |
| 338 'GOOGLE_API_HOSTS': googleApiHosts, | 357 'GOOGLE_API_HOSTS': googleApiHosts, |
| 339 'APP_NAME': app_name, | 358 'APP_NAME': app_name, |
| 340 'APP_DESCRIPTION': app_description, | 359 'APP_DESCRIPTION': app_description, |
| 341 'OAUTH_GDRIVE_SCOPE': '', | 360 'OAUTH_GDRIVE_SCOPE': '', |
| 361 'USE_GCD': use_gcd, | |
| 342 'XMPP_SERVER': xmppServer, | 362 'XMPP_SERVER': xmppServer, |
| 343 } | 363 } |
| 344 if 'GOOGLE_DRIVE' in app_capabilities: | 364 if 'GOOGLE_DRIVE' in app_capabilities: |
| 345 context['OAUTH_GDRIVE_SCOPE'] = ('https://docs.google.com/feeds/ ' | 365 context['OAUTH_GDRIVE_SCOPE'] = ('https://docs.google.com/feeds/ ' |
| 346 'https://www.googleapis.com/auth/drive') | 366 'https://www.googleapis.com/auth/drive') |
| 347 processJinjaTemplate(manifest_template, | 367 processJinjaTemplate(manifest_template, |
| 348 jinja_paths, | 368 jinja_paths, |
| 349 os.path.join(destination, 'manifest.json'), | 369 os.path.join(destination, 'manifest.json'), |
| 350 context) | 370 context) |
| 351 | 371 |
| 352 # Make the zipfile. | 372 # Make the zipfile. |
| 353 createZip(zip_path, destination) | 373 createZip(zip_path, destination) |
| 354 | 374 |
| 355 return 0 | 375 return 0 |
| 356 | 376 |
| 357 | 377 |
| 358 def main(): | 378 def main(): |
| 359 if len(sys.argv) < 6: | 379 parser = argparse.ArgumentParser() |
| 360 print ('Usage: build-webapp.py ' | 380 parser.add_argument('buildtype') |
| 361 '<build-type> <version> <dst> <zip-path> <manifest_template> ' | 381 parser.add_argument('version') |
| 362 '<webapp_type> <other files...> ' | 382 parser.add_argument('destination') |
| 363 '--app_name <name> ' | 383 parser.add_argument('zip_path') |
| 364 '--app_description <description> ' | 384 parser.add_argument('manifest_template') |
| 365 '--app_capabilities <capabilities...> ' | 385 parser.add_argument('webapp_type') |
| 366 '[--appid <appid>] ' | 386 parser.add_argument('files', nargs='*', metavar='file', default=[]) |
| 367 '[--locales_listfile <locales-listfile-name>] ' | 387 parser.add_argument('--app_name', metavar='NAME') |
| 368 '[--jinja_paths <paths...>] ' | 388 parser.add_argument('--app_description', metavar='TEXT') |
| 369 '[--service_environment <service_environment>]') | 389 parser.add_argument('--app_capabilities', |
| 370 return 1 | 390 nargs='*', default=[], metavar='CAPABILITY') |
| 391 parser.add_argument('--appid') | |
| 392 parser.add_argument('--locales_listfile', default='', metavar='PATH') | |
| 393 parser.add_argument('--jinja_paths', nargs='*', default=[], metavar='PATH') | |
| 394 parser.add_argument('--service_environment', default='', metavar='ENV') | |
| 395 parser.add_argument('--use_gcd', choices=['0', '1'], default='0') | |
| 371 | 396 |
| 372 arg_type = '' | 397 args = parser.parse_args() |
| 373 files = [] | 398 args.use_gcd = (args.use_gcd != '0') |
| 374 locales_listfile = '' | 399 args.app_capabilities = set(args.app_capabilities) |
| 375 jinja_paths = [] | 400 return buildWebApp(**args.__dict__) |
|
Sergey Ulanov
2015/03/25 18:56:43
Use dict(args) instead of args.__dict__
John Williams
2015/03/25 20:32:19
Using |dict| doesn't work, but |vars| does.
| |
| 376 app_id = None | |
| 377 app_name = None | |
| 378 app_description = None | |
| 379 app_capabilities = set([]) | |
| 380 service_environment = '' | |
| 381 | |
| 382 for arg in sys.argv[7:]: | |
| 383 if arg in ['--locales_listfile', | |
| 384 '--jinja_paths', | |
| 385 '--appid', | |
| 386 '--app_name', | |
| 387 '--app_description', | |
| 388 '--app_capabilities', | |
| 389 '--service_environment']: | |
| 390 arg_type = arg | |
| 391 elif arg_type == '--locales_listfile': | |
| 392 locales_listfile = arg | |
| 393 arg_type = '' | |
| 394 elif arg_type == '--jinja_paths': | |
| 395 jinja_paths.append(arg) | |
| 396 elif arg_type == '--appid': | |
| 397 app_id = arg | |
| 398 arg_type = '' | |
| 399 elif arg_type == '--app_name': | |
| 400 app_name = arg | |
| 401 arg_type = '' | |
| 402 elif arg_type == '--app_description': | |
| 403 app_description = arg | |
| 404 arg_type = '' | |
| 405 elif arg_type == '--app_capabilities': | |
| 406 app_capabilities.add(arg) | |
| 407 elif arg_type == '--service_environment': | |
| 408 service_environment = arg | |
| 409 arg_type = '' | |
| 410 else: | |
| 411 files.append(arg) | |
| 412 | |
| 413 # Load the locales files from the locales_listfile. | |
| 414 if not locales_listfile: | |
| 415 raise Exception('You must specify a locales_listfile') | |
| 416 locales = [] | |
| 417 with open(locales_listfile) as input: | |
| 418 for s in input: | |
| 419 locales.append(s.rstrip()) | |
| 420 | |
| 421 return buildWebApp(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], | |
| 422 sys.argv[5], sys.argv[6], app_id, app_name, | |
| 423 app_description, app_capabilities, files, locales, | |
| 424 jinja_paths, service_environment) | |
| 425 | 401 |
| 426 | 402 |
| 427 if __name__ == '__main__': | 403 if __name__ == '__main__': |
| 428 sys.exit(main()) | 404 sys.exit(main()) |
| OLD | NEW |