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