84b5fe6c6ce1b57da167472d292e5295164f21a2
[platform/framework/web/crosswalk.git] / src / xwalk / app / tools / android / customize.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2013 Intel Corporation. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 import compress_js_and_css
8 import fnmatch
9 import json
10 import optparse
11 import os
12 import re
13 import shutil
14 import stat
15 import sys
16
17 # get xwalk absolute path so we can run this script from any location
18 xwalk_dir = os.path.dirname(os.path.abspath(__file__))
19 sys.path.append(xwalk_dir)
20
21 from app_info import AppInfo
22 from customize_launch_screen import CustomizeLaunchScreen
23 from handle_xml import AddElementAttribute
24 from handle_xml import AddElementAttributeAndText
25 from handle_xml import EditElementAttribute
26 from handle_xml import EditElementValueByNodeName
27 from handle_permissions import HandlePermissions
28 from xml.dom import minidom
29
30 TEMPLATE_DIR_NAME = 'template'
31
32 def VerifyPackageName(value):
33   regex = r'^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$'
34   descrpt = 'Each part of package'
35   sample = 'org.xwalk.example, org.xwalk.example_'
36
37   if len(value) >= 128:
38     print('To be safe, the length of package name or app name '
39           'should be less than 128.')
40     sys.exit(6)
41
42   if not re.match(regex, value):
43     print('Error: %s name should be started with letters and should not '
44           'contain invalid characters.\n'
45           'It may contain lowercase letters, numbers, blank spaces and '
46           'underscores\n'
47           'Sample: %s' % (descrpt, sample))
48     sys.exit(6)
49
50
51 def ReplaceSpaceWithUnderscore(value):
52   return value.replace(' ', '_')
53
54
55 def ReplaceInvalidChars(value, mode='default'):
56   """ Replace the invalid chars with '_' for input string.
57   Args:
58     value: the original string.
59     mode: the target usage mode of original string.
60   """
61   if mode == 'default':
62     invalid_chars = '\/:*?"<>|- '
63   elif mode == 'apkname':
64     invalid_chars = '\/:.*?"<>|- '
65   for c in invalid_chars:
66     if mode == 'apkname' and c in value:
67       print("Illegal character: '%s' is replaced with '_'" % c)
68     value = value.replace(c, '_')
69   return value
70
71
72 def GetFilesByExt(path, ext, sub_dir=True):
73   if os.path.exists(path):
74     file_list = []
75     for name in os.listdir(path):
76       full_name = os.path.join(path, name)
77       st = os.lstat(full_name)
78       if stat.S_ISDIR(st.st_mode) and sub_dir:
79         file_list += GetFilesByExt(full_name, ext)
80       elif os.path.isfile(full_name):
81         if fnmatch.fnmatch(full_name, ext):
82           file_list.append(full_name)
83     return file_list
84   else:
85     return []
86
87
88 def ParseParameterForCompressor(option, value, values, parser):
89   if ((not values or values.startswith('-'))
90       and value.find('--compressor') != -1):
91     values = 'all'
92   val = values
93   if parser.rargs and not parser.rargs[0].startswith('-'):
94     val = parser.rargs[0]
95     parser.rargs.pop(0)
96   setattr(parser.values, option.dest, val)
97
98
99 def CompressSourceFiles(app_root, compressor):
100   js_list = []
101   css_list = []
102   js_ext = '*.js'
103   css_ext = '*.css'
104
105   if compressor == 'all' or compressor == 'js':
106     js_list = GetFilesByExt(app_root, js_ext)
107     compress_js_and_css.CompressJavaScript(js_list)
108
109   if compressor == 'all' or compressor == 'css':
110     css_list = GetFilesByExt(app_root, css_ext)
111     compress_js_and_css.CompressCss(css_list)
112
113
114 def Prepare(app_info, compressor):
115   """Copy the Android template project to a new app project
116      named app_info.app_name
117   """
118   # create new app_dir in xwalk_dir
119   app_name = app_info.android_name
120   app_dir = os.path.join(xwalk_dir, app_name)
121   app_package = app_info.package
122   app_root = app_info.app_root
123   template_app_dir = os.path.join(xwalk_dir, TEMPLATE_DIR_NAME)
124
125   # 1) copy template project to app_dir
126   if os.path.exists(app_dir):
127     shutil.rmtree(app_dir)
128   shutil.copytree(template_app_dir, app_dir)
129
130   # 2) replace app_dir 'src' dir with template 'src' dir
131   shutil.rmtree(os.path.join(app_dir, 'src'))
132   template_src_root = os.path.join(template_app_dir, 'src', 'org', 'xwalk',
133                                    'app', 'template')
134
135   # 3) Create directory tree from app package (org.xyz.foo -> src/org/xyz/foo)
136   #    and copy AppTemplateActivity.java to <app_name>Activity.java 
137   template_activity_file = os.path.join(template_src_root,
138                                         'AppTemplateActivity.java')
139   if not os.path.isfile(template_activity_file):
140     print ('Error: The template file %s was not found. '
141            'Please make sure this file exists.' % template_activity_file)
142     sys.exit(7)
143   app_pkg_dir = os.path.join(app_dir, 'src',
144                              app_package.replace('.', os.path.sep))
145   if not os.path.exists(app_pkg_dir):
146     os.makedirs(app_pkg_dir)
147   app_activity_file = app_name + 'Activity.java'
148   shutil.copyfile(template_activity_file,
149                   os.path.join(app_pkg_dir, app_activity_file))
150
151   # 4) Copy all HTML source from app_root to app_dir
152   if app_root:
153     app_assets_dir = os.path.join(app_dir, 'assets', 'www')
154     if os.path.isdir(app_assets_dir):
155       shutil.rmtree(app_assets_dir)
156     shutil.copytree(app_root, app_assets_dir)
157     if compressor:
158       CompressSourceFiles(app_assets_dir, compressor)
159
160
161 def EncodingUnicodeValue(value):
162   try:
163     if isinstance(value, unicode):
164       value = value.encode("utf-8")
165   except NameError:
166     pass
167   return value
168
169
170 def CustomizeStringXML(name, description):
171   strings_path = os.path.join(xwalk_dir, name, 'res', 'values', 'strings.xml')
172   if not os.path.isfile(strings_path):
173     print ('Please make sure strings_xml'
174            ' exists under template folder.')
175     sys.exit(6)
176
177   if description:
178     description = EncodingUnicodeValue(description)
179     xmldoc = minidom.parse(strings_path)
180     AddElementAttributeAndText(xmldoc, 'string', 'name', 'description',
181                                description)
182     strings_file = open(strings_path, 'w')
183     xmldoc.writexml(strings_file, encoding='utf-8')
184     strings_file.close()
185
186
187 def CustomizeThemeXML(name, fullscreen, manifest):
188   theme_path = os.path.join(xwalk_dir, name, 'res', 'values-v14', 'theme.xml')
189   if not os.path.isfile(theme_path):
190     print('Error: theme.xml is missing in the build tool.')
191     sys.exit(6)
192
193   theme_xmldoc = minidom.parse(theme_path)
194   if fullscreen:
195     EditElementValueByNodeName(theme_xmldoc, 'item',
196                                'android:windowFullscreen', 'true')
197   has_background = CustomizeLaunchScreen(manifest, name)
198   if has_background:
199     EditElementValueByNodeName(theme_xmldoc, 'item',
200                                'android:windowBackground',
201                                '@drawable/launchscreen_bg')
202   theme_file = open(theme_path, 'w')
203   theme_xmldoc.writexml(theme_file, encoding='utf-8')
204   theme_file.close()
205
206
207 def CustomizeXML(app_info, description, icon_dict, manifest, permissions):
208   app_version = app_info.app_version
209   app_versionCode = app_info.app_versionCode
210   name = app_info.android_name
211   orientation = app_info.orientation
212   package = app_info.package
213   app_name = app_info.app_name
214   # Chinese character with unicode get from 'manifest.json' will cause
215   # 'UnicodeEncodeError' when finally wrote to 'AndroidManifest.xml'.
216   app_name = EncodingUnicodeValue(app_name)
217   # If string start with '@' or '?', it will be treated as Android resource,
218   # which will cause 'No resource found' error,
219   # append a space before '@' or '?' to fix that.
220   if app_name.startswith('@') or app_name.startswith('?'):
221     app_name = ' ' + app_name
222   manifest_path = os.path.join(xwalk_dir, name, 'AndroidManifest.xml')
223   if not os.path.isfile(manifest_path):
224     print ('Please make sure AndroidManifest.xml'
225            ' exists under template folder.')
226     sys.exit(6)
227
228   CustomizeStringXML(name, description)
229   CustomizeThemeXML(name, app_info.fullscreen_flag, manifest)
230   xmldoc = minidom.parse(manifest_path)
231   EditElementAttribute(xmldoc, 'manifest', 'package', package)
232   if app_versionCode:
233     EditElementAttribute(xmldoc, 'manifest', 'android:versionCode',
234                          str(app_versionCode))
235   if app_version:
236     EditElementAttribute(xmldoc, 'manifest', 'android:versionName',
237                          app_version)
238   if description:
239     EditElementAttribute(xmldoc, 'manifest', 'android:description',
240                          "@string/description")
241   HandlePermissions(permissions, xmldoc)
242   EditElementAttribute(xmldoc, 'application', 'android:label', app_name)
243   activity_name = package + '.' + name + 'Activity'
244   EditElementAttribute(xmldoc, 'activity', 'android:name', activity_name)
245   EditElementAttribute(xmldoc, 'activity', 'android:label', app_name)
246   if orientation:
247     EditElementAttribute(xmldoc, 'activity', 'android:screenOrientation',
248                          orientation)
249   icon_name = CustomizeIcon(name, app_info.app_root, app_info.icon, icon_dict)
250   if icon_name:
251     EditElementAttribute(xmldoc, 'application', 'android:icon',
252                          '@drawable/%s' % icon_name)
253
254   file_handle = open(os.path.join(xwalk_dir, name, 'AndroidManifest.xml'), 'w')
255   xmldoc.writexml(file_handle, encoding='utf-8')
256   file_handle.close()
257
258
259 def ReplaceString(file_path, src, dest):
260   file_handle = open(file_path, 'r')
261   src_content = file_handle.read()
262   file_handle.close()
263   file_handle = open(file_path, 'w')
264   dest_content = src_content.replace(src, dest)
265   file_handle.write(dest_content)
266   file_handle.close()
267
268
269 def SetVariable(file_path, string_line, variable, value):
270   function_string = ('%sset%s(%s);\n' %
271                     ('        ', variable, value))
272   temp_file_path = file_path + '.backup'
273   file_handle = open(temp_file_path, 'w+')
274   for line in open(file_path):
275     file_handle.write(line)
276     if (line.find(string_line) >= 0):
277       file_handle.write(function_string)
278   file_handle.close()
279   shutil.move(temp_file_path, file_path)
280
281
282 def CustomizeJava(app_info, app_url, app_local_path, keep_screen_on):
283   name = app_info.android_name
284   package = app_info.package
285   app_pkg_dir = os.path.join(xwalk_dir, name, 'src',
286                              package.replace('.', os.path.sep))
287   dest_activity = os.path.join(app_pkg_dir, name + 'Activity.java')
288   ReplaceString(dest_activity, 'org.xwalk.app.template', package)
289   ReplaceString(dest_activity, 'AppTemplate', name)
290   manifest_file = os.path.join(xwalk_dir, name, 'assets/www', 'manifest.json')
291   if os.path.isfile(manifest_file):
292     ReplaceString(
293         dest_activity,
294         'loadAppFromUrl("file:///android_asset/www/index.html")',
295         'loadAppFromManifest("file:///android_asset/www/manifest.json")')
296   else:
297     if app_url:
298       if re.search(r'^http(|s)', app_url):
299         ReplaceString(dest_activity, 'file:///android_asset/www/index.html',
300                       app_url)
301     elif app_local_path:
302       if os.path.isfile(os.path.join(xwalk_dir, name, 'assets/www',
303                                      app_local_path)):
304         ReplaceString(dest_activity, 'file:///android_asset/www/index.html',
305                       'app://' + package + '/' + app_local_path)
306       else:
307         print ('Please make sure that the relative path of entry file'
308                ' is correct.')
309         sys.exit(8)
310
311   if app_info.remote_debugging:
312     SetVariable(dest_activity,
313                 'public void onCreate(Bundle savedInstanceState)',
314                 'RemoteDebugging', 'true')
315   if app_info.fullscreen_flag:
316     SetVariable(dest_activity,
317                 'super.onCreate(savedInstanceState)',
318                 'IsFullscreen', 'true')
319   if keep_screen_on:
320     ReplaceString(
321         dest_activity,
322         'super.onCreate(savedInstanceState);',
323         'super.onCreate(savedInstanceState);\n        ' +
324         'getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);')
325
326
327 def CopyExtensionFile(extension_name, suffix, src_path, dest_path):
328   # Copy the file from src_path into dest_path.
329   dest_extension_path = os.path.join(dest_path, extension_name)
330   if os.path.exists(dest_extension_path):
331     # TODO: Refine it by renaming it internally.
332     print('Error: duplicated extension names "%s" are found. Please rename it.'
333           % extension_name)
334     sys.exit(9)
335   else:
336     os.mkdir(dest_extension_path)
337
338   file_name = extension_name + suffix
339   src_file = os.path.join(src_path, file_name)
340   dest_file = os.path.join(dest_extension_path, file_name)
341   if not os.path.isfile(src_file):
342     print('Error: %s was not found in %s.' % (file_name, src_path))
343     sys.exit(9)
344   else:
345     shutil.copyfile(src_file, dest_file)
346
347
348 def CustomizeExtensions(app_info, extensions):
349   """Copy the files from external extensions and merge them into APK.
350
351   The directory of one external extension should be like:
352     myextension/
353       myextension.jar
354       myextension.js
355       myextension.json
356   That means the name of the internal files should be the same as the
357   directory name.
358   For .jar files, they'll be copied to xwalk-extensions/ and then
359   built into classes.dex in make_apk.py.
360   For .js files, they'll be copied into assets/xwalk-extensions/.
361   For .json files, the'll be merged into one file called
362   extensions-config.json and copied into assets/.
363   """
364   if not extensions:
365     return
366   name = app_info.android_name
367   apk_path = os.path.join(xwalk_dir, name)
368   apk_assets_path = os.path.join(apk_path, 'assets')
369   extensions_string = 'xwalk-extensions'
370
371   # Set up the target directories and files.
372   dest_jar_path = os.path.join(apk_path, extensions_string)
373   os.mkdir(dest_jar_path)
374   dest_js_path = os.path.join(apk_assets_path, extensions_string)
375   os.mkdir(dest_js_path)
376   apk_extensions_json_path = os.path.join(apk_assets_path,
377                                           'extensions-config.json')
378
379   # Split the paths into a list.
380   extension_paths = extensions.split(os.pathsep)
381   extension_json_list = []
382   for source_path in extension_paths:
383     if not os.path.exists(source_path):
384       print('Error: can\'t find the extension directory \'%s\'.' % source_path)
385       sys.exit(9)
386     # Remove redundant separators to avoid empty basename.
387     source_path = os.path.normpath(source_path)
388     extension_name = os.path.basename(source_path)
389
390     # Copy .jar file into xwalk-extensions.
391     CopyExtensionFile(extension_name, '.jar', source_path, dest_jar_path)
392
393     # Copy .js file into assets/xwalk-extensions.
394     CopyExtensionFile(extension_name, '.js', source_path, dest_js_path)
395
396     # Merge .json file into assets/xwalk-extensions.
397     file_name = extension_name + '.json'
398     src_file = os.path.join(source_path, file_name)
399     if not os.path.isfile(src_file):
400       print('Error: %s is not found in %s.' % (file_name, source_path))
401       sys.exit(9)
402     else:
403       src_file_handle = open(src_file)
404       src_file_content = src_file_handle.read()
405       json_output = json.JSONDecoder().decode(src_file_content)
406       # Below 3 properties are used by runtime. See extension manager.
407       # And 'permissions' will be merged.
408       if not ('name' in json_output and
409               'class' in json_output and
410               'jsapi' in json_output):
411         print ('Error: properties \'name\', \'class\' and \'jsapi\' in a json '
412                'file are mandatory.')
413         sys.exit(9)
414       # Reset the path for JavaScript.
415       js_path_prefix = extensions_string + '/' + extension_name + '/'
416       json_output['jsapi'] = js_path_prefix + json_output['jsapi']
417       extension_json_list.append(json_output)
418       # Merge the permissions of extensions into AndroidManifest.xml.
419       manifest_path = os.path.join(xwalk_dir, name, 'AndroidManifest.xml')
420       xmldoc = minidom.parse(manifest_path)
421       if ('permissions' in json_output):
422         # Get used permission list to avoid repetition as "--permissions"
423         # option can also be used to declare used permissions.
424         existingList = []
425         usedPermissions = xmldoc.getElementsByTagName("uses-permission")
426         for used in usedPermissions:
427           existingList.append(used.getAttribute("android:name"))
428
429         # Add the permissions to manifest file if not used yet.
430         for p in json_output['permissions']:
431           if p in existingList:
432             continue
433           AddElementAttribute(xmldoc, 'uses-permission', 'android:name', p)
434           existingList.append(p)
435
436         # Write to the manifest file to save the update.
437         file_handle = open(manifest_path, 'w')
438         xmldoc.writexml(file_handle, encoding='utf-8')
439         file_handle.close()
440
441   # Write configuration of extensions into the target extensions-config.json.
442   if extension_json_list:
443     extensions_string = json.JSONEncoder().encode(extension_json_list)
444     extension_json_file = open(apk_extensions_json_path, 'w')
445     extension_json_file.write(extensions_string)
446     extension_json_file.close()
447
448
449 def GenerateCommandLineFile(app_info, xwalk_command_line):
450   if xwalk_command_line == '':
451     return
452   assets_path = os.path.join(xwalk_dir, app_info.android_name, 'assets')
453   file_path = os.path.join(assets_path, 'xwalk-command-line')
454   command_line_file = open(file_path, 'w')
455   command_line_file.write('xwalk ' + xwalk_command_line)
456
457
458 def CustomizeIconByDict(name, app_root, icon_dict):
459   icon_name = None
460   drawable_dict = {'ldpi': [1, 37], 'mdpi': [37, 72], 'hdpi': [72, 96],
461                    'xhdpi': [96, 120], 'xxhdpi': [120, 144],
462                    'xxxhdpi': [144, 168]}
463   if not icon_dict:
464     return icon_name
465
466   try:
467     icon_dict = dict((int(k), v) for k, v in icon_dict.items())
468   except ValueError:
469     print('The key of icon in the manifest file should be a number.')
470
471   if len(icon_dict) > 0:
472     icon_list = sorted(icon_dict.items(), key=lambda d: d[0])
473     for kd, vd in drawable_dict.items():
474       for item in icon_list:
475         if item[0] >= vd[0] and item[0] < vd[1]:
476           drawable_path = os.path.join(xwalk_dir, name, 'res', 'drawable-' + kd)
477           if not os.path.exists(drawable_path):
478             os.makedirs(drawable_path)
479           icon = os.path.join(app_root, item[1])
480           if icon and os.path.isfile(icon):
481             icon_name = os.path.basename(icon)
482             icon_suffix = icon_name.split('.')[-1]
483             shutil.copyfile(icon, os.path.join(drawable_path,
484                                                'icon.' + icon_suffix))
485             icon_name = 'icon'
486           elif icon and (not os.path.isfile(icon)):
487             print('Error: "%s" does not exist.' % icon)
488             sys.exit(6)
489           break
490   return icon_name
491
492
493 def CustomizeIconByOption(name, icon):
494   if os.path.isfile(icon):
495     drawable_path = os.path.join(xwalk_dir, name, 'res', 'drawable')
496     if not os.path.exists(drawable_path):
497       os.makedirs(drawable_path)
498     icon_file = os.path.basename(icon)
499     icon_file = ReplaceInvalidChars(icon_file)
500     shutil.copyfile(icon, os.path.join(drawable_path, icon_file))
501     icon_name = os.path.splitext(icon_file)[0]
502     return icon_name
503   else:
504     print('Error: "%s" does not exist.')
505     sys.exit(6)
506
507
508 def CustomizeIcon(name, app_root, icon, icon_dict):
509   icon_name = None
510   if icon:
511     icon_name = CustomizeIconByOption(name, icon)
512   else:
513     icon_name = CustomizeIconByDict(name, app_root, icon_dict)
514   return icon_name
515
516
517 def CustomizeAll(app_info, description, icon_dict, permissions, app_url,
518                  app_local_path, keep_screen_on, extensions, manifest,
519                  xwalk_command_line='', compressor=None):
520   try:
521     Prepare(app_info, compressor)
522     CustomizeXML(app_info, description, icon_dict, manifest, permissions)
523     CustomizeJava(app_info, app_url, app_local_path, keep_screen_on)
524     CustomizeExtensions(app_info, extensions)
525     GenerateCommandLineFile(app_info, xwalk_command_line)
526   except SystemExit as ec:
527     print('Exiting with error code: %d' % ec.code)
528     sys.exit(ec.code)
529
530
531 def main():
532   parser = optparse.OptionParser()
533   info = ('The package name. Such as: '
534           '--package=com.example.YourPackage')
535   parser.add_option('--package', help=info)
536   info = ('The apk name. Such as: --name="Your Application Name"')
537   parser.add_option('--name', help=info)
538   info = ('The version of the app. Such as: --app-version=TheVersionNumber')
539   parser.add_option('--app-version', help=info)
540   info = ('The versionCode of the app. Such as: --app-versionCode=24')
541   parser.add_option('--app-versionCode', type='int', help=info)
542   info = ('The application description. Such as:'
543           '--description=YourApplicationdDescription')
544   parser.add_option('--description', help=info)
545   info = ('The permission list. Such as: --permissions="geolocation"'
546           'For more permissions, such as:'
547           '--permissions="geolocation:permission2"')
548   parser.add_option('--permissions', help=info)
549   info = ('The url of application. '
550           'This flag allows to package website as apk. Such as: '
551           '--app-url=http://www.intel.com')
552   parser.add_option('--app-url', help=info)
553   info = ('The root path of the web app. '
554           'This flag allows to package local web app as apk. Such as: '
555           '--app-root=/root/path/of/the/web/app')
556   parser.add_option('--app-root', help=info)
557   info = ('The reletive path of entry file based on |app_root|. '
558           'This flag should work with "--app-root" together. '
559           'Such as: --app-local-path=/reletive/path/of/entry/file')
560   parser.add_option('--app-local-path', help=info)
561   parser.add_option('--enable-remote-debugging', action='store_true',
562                     dest='enable_remote_debugging', default=False,
563                     help='Enable remote debugging.')
564   parser.add_option('-f', '--fullscreen', action='store_true',
565                     dest='fullscreen', default=False,
566                     help='Make application fullscreen.')
567   parser.add_option('--keep-screen-on', action='store_true', default=False,
568                     help='Support keeping screen on')
569   info = ('The path list for external extensions separated by os separator.'
570           'On Linux and Mac, the separator is ":". On Windows, it is ";".'
571           'Such as: --extensions="/path/to/extension1:/path/to/extension2"')
572   parser.add_option('--extensions', help=info)
573   info = ('The orientation of the web app\'s display on the device. '
574           'Such as: --orientation=landscape. The default value is "unspecified"'
575           'The value options are the same as those on the Android: '
576           'http://developer.android.com/guide/topics/manifest/'
577           'activity-element.html#screen')
578   parser.add_option('--orientation', help=info)
579   parser.add_option('--manifest', help='The manifest path')
580   info = ('Use command lines.'
581           'Crosswalk is powered by Chromium and supports Chromium command line.'
582           'For example, '
583           '--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
584   parser.add_option('--xwalk-command-line', default='', help=info)
585   info = ('Minify and obfuscate javascript and css.'
586           '--compressor: compress javascript and css.'
587           '--compressor=js: compress javascript.'
588           '--compressor=css: compress css.')
589   parser.add_option('--compressor', dest='compressor', action='callback',
590                     callback=ParseParameterForCompressor,
591                     type='string', nargs=0, help=info)
592   options, _ = parser.parse_args()
593   try:
594     icon_dict = {144: 'icons/icon_144.png',
595                  72: 'icons/icon_72.png',
596                  96: 'icons/icon_96.png',
597                  48: 'icons/icon_48.png'}
598     app_info = AppInfo()
599     if options.name is not None:
600       app_info.android_name = options.name
601     if options.app_root is None:
602       app_info.app_root = os.path.join(xwalk_dir, 'test_data', 'manifest')
603     else:
604       app_info.app_root = options.app_root
605     if options.package is not None:
606       app_info.package = options.package
607     if options.orientation is not None:
608       app_info.orientation = options.orientation
609     if options.app_version is not None:
610       app_info.app_version = options.app_version
611     if options.enable_remote_debugging is not None:
612       app_info.remote_debugging = options.enable_remote_debugging
613     if options.fullscreen is not None:
614       app_info.fullscreen_flag = options.fullscreen
615     app_info.icon = os.path.join('test_data', 'manifest', 'icons',
616                                  'icon_96.png')
617     CustomizeAll(app_info, options.description, icon_dict,
618                  options.permissions, options.app_url, options.app_local_path,
619                  options.keep_screen_on, options.extensions, None,
620                  options.xwalk_command_line, options.compressor)
621   except SystemExit as ec:
622     print('Exiting with error code: %d' % ec.code)
623     return ec.code
624   return 0
625
626
627 if __name__ == '__main__':
628   sys.exit(main())