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 # pylint: disable=F0401
16 sys.path.append('scripts/gyp')
18 from app_info import AppInfo
19 from customize import VerifyAppName, CustomizeAll, \
20 ParseParameterForCompressor, ReplaceSpaceWithUnderscore
21 from dex import AddExeExtensions
22 from handle_permissions import permission_mapping_table
23 from manifest_json_parser import HandlePermissionList
24 from manifest_json_parser import ManifestJsonParser
28 if os.path.exists(path):
31 def AllArchitectures():
34 def RunCommand(command, verbose=False, shell=False):
35 """Runs the command list, print the output, and propagate its result."""
36 proc = subprocess.Popen(command, stdout=subprocess.PIPE,
37 stderr=subprocess.STDOUT, shell=shell)
39 output = proc.communicate()[0]
40 result = proc.returncode
42 print(output.decode("utf-8").strip())
44 print ('Command "%s" exited with non-zero exit code %d'
45 % (' '.join(command), result))
50 """Search PATH for executable files with the given name."""
52 exts = [_f for _f in os.environ.get('PATHEXT', '').split(os.pathsep) if _f]
53 path = os.environ.get('PATH', None)
56 for p in os.environ.get('PATH', '').split(os.pathsep):
57 p = os.path.join(p, name)
58 if os.access(p, os.X_OK):
62 if os.access(pext, os.X_OK):
68 """Find executable file with the given name
69 and maximum API level under specific path."""
71 for root, _, files in os.walk(path):
73 key = os.path.join(root, name)
74 sdk_version = os.path.basename(os.path.dirname(key))
75 str_num = re.search(r'\d+', sdk_version)
77 result[key] = int(str_num.group())
82 return max(iter(result.items()), key=operator.itemgetter(1))[0]
86 """Get the version of this python tool."""
87 version_str = 'Crosswalk app packaging tool version is '
88 file_handle = open(path, 'r')
89 src_content = file_handle.read()
90 version_nums = re.findall(r'\d+', src_content)
91 version_str += ('.').join(version_nums)
96 def ParseManifest(options, app_info):
97 parser = ManifestJsonParser(os.path.expanduser(options.manifest))
98 original_name = app_info.original_name = parser.GetAppName()
101 VerifyAppName(options.package, 'packagename')
103 VerifyAppName(original_name)
104 app_name = ReplaceSpaceWithUnderscore(original_name)
105 options.package = 'org.xwalk.' + app_name.lower()
107 VerifyAppName(options.name)
108 app_info.original_name = options.name
109 options.name = ReplaceSpaceWithUnderscore(options.name)
111 VerifyAppName(original_name)
112 options.name = ReplaceSpaceWithUnderscore(original_name)
113 if not options.app_version:
114 options.app_version = parser.GetVersion()
115 if not options.app_versionCode and not options.app_versionCodeBase:
116 options.app_versionCode = 1
117 if parser.GetDescription():
118 options.description = parser.GetDescription()
119 if parser.GetPermissions():
120 options.permissions = parser.GetPermissions()
121 if parser.GetAppUrl():
122 options.app_url = parser.GetAppUrl()
123 elif parser.GetAppLocalPath():
124 options.app_local_path = parser.GetAppLocalPath()
126 print('Error: there is no app launch path defined in manifest.json.')
128 if parser.GetAppRoot():
129 options.app_root = parser.GetAppRoot()
130 options.icon_dict = parser.GetIcons()
131 if parser.GetFullScreenFlag().lower() == 'true':
132 options.fullscreen = True
133 elif parser.GetFullScreenFlag().lower() == 'false':
134 options.fullscreen = False
137 def ParseXPK(options, out_dir):
138 cmd = ['python', 'parse_xpk.py',
139 '--file=%s' % os.path.expanduser(options.xpk),
140 '--out=%s' % out_dir]
143 print ('Use the manifest from XPK by default '
144 'when "--xpk" option is specified, and '
145 'the "--manifest" option would be ignored.')
148 if os.path.isfile(os.path.join(out_dir, 'manifest.json')):
149 options.manifest = os.path.join(out_dir, 'manifest.json')
151 print('XPK doesn\'t contain manifest file.')
155 def FindExtensionJars(root_path):
156 ''' Find all .jar files for external extensions. '''
158 if not os.path.exists(root_path):
159 return extension_jars
161 for afile in os.listdir(root_path):
162 if os.path.isdir(os.path.join(root_path, afile)):
163 base_name = os.path.basename(afile)
164 extension_jar = os.path.join(root_path, afile, base_name + '.jar')
165 if os.path.isfile(extension_jar):
166 extension_jars.append(extension_jar)
167 return extension_jars
170 # Follows the recommendation from
171 # http://software.intel.com/en-us/blogs/2012/11/12/how-to-publish-
172 # your-apps-on-google-play-for-x86-based-android-devices-using
173 def MakeVersionCode(options):
174 ''' Construct a version code'''
175 if options.app_versionCode:
176 return options.app_versionCode
178 # First digit is ABI, ARM=2, x86=6
180 if options.arch == 'arm':
182 if options.arch == 'x86':
185 if options.app_versionCodeBase:
186 b = str(options.app_versionCodeBase)
188 print('Version code base must be 7 digits or less: '
189 'versionCodeBase=%s' % (b))
191 # zero pad to 7 digits, middle digits can be used for other
192 # features, according to recommendation in URL
193 return '%s%s' % (abi, b.zfill(7))
196 def Customize(options, app_info):
198 app_info.package = options.package
200 app_info.name = options.name
201 if options.app_version:
202 app_info.app_version = options.app_version
203 app_info.app_versionCode = MakeVersionCode(options)
205 app_info.app_root = os.path.expanduser(options.app_root)
206 if options.enable_remote_debugging:
207 app_info.remote_debugging = '--enable-remote-debugging'
208 if options.fullscreen:
209 app_info.fullscreen_flag = '-f'
210 if options.orientation:
211 app_info.orientation = options.orientation
213 app_info.icon = '%s' % os.path.expanduser(options.icon)
214 CustomizeAll(app_info, options.description, options.icon_dict,
215 options.permissions, options.app_url, options.app_local_path,
216 options.keep_screen_on, options.extensions, options.manifest,
217 options.xwalk_command_line, options.compressor)
220 def Execution(options, name):
221 android_path_array = Which('android')
222 if not android_path_array:
223 print('Please install Android SDK first.')
226 sdk_root_path = os.path.dirname(os.path.dirname(android_path_array[0]))
229 sdk_jar_path = Find('android.jar',
230 os.path.join(sdk_root_path, 'platforms'))
232 print('Your Android SDK may be ruined, please reinstall it.')
235 level_string = os.path.basename(os.path.dirname(sdk_jar_path))
236 api_level = int(re.search(r'\d+', level_string).group())
238 print('Please install Android API level (>=14) first.')
241 if options.keystore_path:
242 key_store = os.path.expanduser(options.keystore_path)
243 if options.keystore_alias:
244 key_alias = options.keystore_alias
246 print('Please provide an alias name of the developer key.')
248 if options.keystore_passcode:
249 key_code = options.keystore_passcode
251 print('Please provide the passcode of the developer key.')
254 print ('Use xwalk\'s keystore by default for debugging. '
255 'Please switch to your keystore when distributing it to app market.')
256 key_store = 'scripts/ant/xwalk-debug.keystore'
257 key_alias = 'xwalkdebugkey'
258 key_code = 'xwalkdebug'
260 if not os.path.exists('out'):
263 # Make sure to use ant-tasks.jar correctly.
264 # Default Android SDK names it as ant-tasks.jar
265 # Chrome third party Android SDk names it as anttasks.jar
266 ant_tasks_jar_path = os.path.join(sdk_root_path,
267 'tools', 'lib', 'ant-tasks.jar')
268 if not os.path.exists(ant_tasks_jar_path):
269 ant_tasks_jar_path = os.path.join(sdk_root_path,
270 'tools', 'lib', 'anttasks.jar')
273 for aapt_str in AddExeExtensions('aapt'):
275 aapt_path = Find(aapt_str, sdk_root_path)
276 print('Use %s in %s.' % (aapt_str, sdk_root_path))
279 print('There doesn\'t exist %s in %s.' % (aapt_str, sdk_root_path))
281 print('Your Android SDK may be ruined, please reinstall it.')
284 # Check whether ant is installed.
286 cmd = ['ant', '-version']
287 RunCommand(cmd, shell=True)
288 except EnvironmentError:
289 print('Please install ant first.')
292 res_dirs = '-DADDITIONAL_RES_DIRS=\'\''
293 res_packages = '-DADDITIONAL_RES_PACKAGES=\'\''
294 res_r_text_files = '-DADDITIONAL_R_TEXT_FILES=\'\''
295 if options.mode == 'embedded':
296 # Prepare the .pak file for embedded mode.
297 pak_src_path = os.path.join('native_libs_res', 'xwalk.pak')
298 pak_des_path = os.path.join(name, 'assets', 'xwalk.pak')
299 shutil.copy(pak_src_path, pak_des_path)
301 # Prepare the icudtl.dat for embedded mode.
302 icudtl_src_path = os.path.join('native_libs_res', 'icudtl.dat')
303 icudtl_des_path = os.path.join(name, 'assets', 'icudtl.dat')
304 shutil.copy(icudtl_src_path, icudtl_des_path)
306 js_src_dir = os.path.join('native_libs_res', 'jsapi')
307 js_des_dir = os.path.join(name, 'assets', 'jsapi')
308 if os.path.exists(js_des_dir):
309 shutil.rmtree(js_des_dir)
310 shutil.copytree(js_src_dir, js_des_dir)
312 res_ui_java = os.path.join('gen', 'ui_java')
313 res_content_java = os.path.join('gen', 'content_java')
314 res_xwalk_java = os.path.join('gen', 'xwalk_core_java')
315 res_dirs = ('-DADDITIONAL_RES_DIRS='
316 + os.path.join(res_ui_java, 'res_crunched') + ' '
317 + os.path.join(res_ui_java, 'res_v14_compatibility') + ' '
318 + os.path.join(res_ui_java, 'res_grit') + ' '
319 + os.path.join('libs_res', 'ui') + ' '
320 + os.path.join(res_content_java, 'res_crunched') + ' '
321 + os.path.join(res_content_java, 'res_v14_compatibility') + ' '
322 + os.path.join('libs_res', 'content') + ' '
323 + os.path.join(res_content_java, 'res_grit') + ' '
324 + os.path.join(res_xwalk_java, 'res_crunched') + ' '
325 + os.path.join(res_xwalk_java, 'res_v14_compatibility') + ' '
326 + os.path.join('libs_res', 'runtime') + ' '
327 + os.path.join(res_xwalk_java, 'res_grit'))
328 res_packages = ('-DADDITIONAL_RES_PACKAGES=org.chromium.ui '
329 'org.xwalk.core org.chromium.content')
330 res_r_text_files = ('-DADDITIONAL_R_TEXT_FILES='
331 + os.path.join(res_ui_java, 'java_R', 'R.txt') + ' '
332 + os.path.join(res_xwalk_java, 'java_R', 'R.txt') + ' '
333 + os.path.join(res_content_java, 'java_R', 'R.txt'))
335 resource_dir = '-DRESOURCE_DIR=' + os.path.join(name, 'res')
336 manifest_path = os.path.join(name, 'AndroidManifest.xml')
337 cmd = ['python', os.path.join('scripts', 'gyp', 'ant.py'),
338 '-DAAPT_PATH=%s' % aapt_path,
342 '-DANDROID_MANIFEST=%s' % manifest_path,
343 '-DANDROID_SDK_JAR=%s' % sdk_jar_path,
344 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
345 '-DANDROID_SDK_VERSION=%d' % api_level,
346 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
347 '-DLIBRARY_MANIFEST_PATHS= ',
350 '-DSTAMP=codegen.stamp',
353 os.path.join('scripts', 'ant', 'apk-codegen.xml')]
354 RunCommand(cmd, options.verbose)
356 # Check whether java is installed.
358 cmd = ['java', '-version']
359 RunCommand(cmd, shell=True)
360 except EnvironmentError:
361 print('Please install Oracle JDK first.')
364 # Compile App source code with app runtime code.
365 classpath = '--classpath='
366 classpath += os.path.join(os.getcwd(), 'libs',
367 'xwalk_app_runtime_java.jar')
368 classpath += ' ' + sdk_jar_path
369 src_dirs = '--src-dirs=' + os.path.join(os.getcwd(), name, 'src') +\
370 ' ' + os.path.join(os.getcwd(), 'out', 'gen')
371 cmd = ['python', os.path.join('scripts', 'gyp', 'javac.py'),
372 '--output-dir=%s' % os.path.join('out', 'classes'),
377 '--stamp=compile.stam']
378 RunCommand(cmd, options.verbose)
381 asset_dir = '-DASSET_DIR=%s' % os.path.join(name, 'assets')
382 xml_path = os.path.join('scripts', 'ant', 'apk-package-resources.xml')
383 cmd = ['python', os.path.join('scripts', 'gyp', 'ant.py'),
384 '-DAAPT_PATH=%s' % aapt_path,
388 '-DANDROID_SDK_JAR=%s' % sdk_jar_path,
389 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
390 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
391 '-DAPK_NAME=%s' % name,
392 '-DAPP_MANIFEST_VERSION_CODE=0',
393 '-DAPP_MANIFEST_VERSION_NAME=Developer Build',
395 '-DCONFIGURATION_NAME=Release',
398 '-DSTAMP=package_resources.stamp',
402 RunCommand(cmd, options.verbose)
404 dex_path = '--dex-path=' + os.path.join(os.getcwd(), 'out', 'classes.dex')
405 app_runtime_jar = os.path.join(os.getcwd(),
406 'libs', 'xwalk_app_runtime_java.jar')
408 # Check whether external extensions are included.
409 extensions_string = 'xwalk-extensions'
410 extensions_dir = os.path.join(os.getcwd(), name, extensions_string)
411 external_extension_jars = FindExtensionJars(extensions_dir)
413 if options.mode == 'embedded':
414 input_jars.append(os.path.join(os.getcwd(), 'libs',
415 'xwalk_runtime_embedded.dex.jar'))
416 dex_command_list = ['python', os.path.join('scripts', 'gyp', 'dex.py'),
418 '--android-sdk-root=%s' % sdk_root_path,
420 os.path.join(os.getcwd(), 'out', 'classes')]
421 dex_command_list.extend(external_extension_jars)
422 dex_command_list.extend(input_jars)
423 RunCommand(dex_command_list)
425 src_dir = '-DSOURCE_DIR=' + os.path.join(name, 'src')
426 apk_path = '-DUNSIGNED_APK_PATH=' + os.path.join('out', 'app-unsigned.apk')
427 native_lib_path = '-DNATIVE_LIBS_DIR='
428 if options.mode == 'embedded':
429 if options.arch == 'x86':
430 x86_native_lib_path = os.path.join('native_libs', 'x86', 'libs',
431 'x86', 'libxwalkcore.so')
432 if os.path.isfile(x86_native_lib_path):
433 native_lib_path += os.path.join('native_libs', 'x86', 'libs')
435 print('Missing x86 native library for Crosswalk embedded APK. Abort!')
437 elif options.arch == 'arm':
438 arm_native_lib_path = os.path.join('native_libs', 'armeabi-v7a', 'libs',
439 'armeabi-v7a', 'libxwalkcore.so')
440 if os.path.isfile(arm_native_lib_path):
441 native_lib_path += os.path.join('native_libs', 'armeabi-v7a', 'libs')
443 print('Missing ARM native library for Crosswalk embedded APK. Abort!')
445 # A space is needed for Windows.
446 native_lib_path += ' '
447 cmd = ['python', 'scripts/gyp/ant.py',
448 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
449 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
450 '-DAPK_NAME=%s' % name,
451 '-DCONFIGURATION_NAME=Release',
458 'scripts/ant/apk-package.xml']
459 RunCommand(cmd, options.verbose)
461 apk_path = '--unsigned-apk-path=' + os.path.join('out', 'app-unsigned.apk')
462 final_apk_path = '--final-apk-path=' + \
463 os.path.join('out', name + '.apk')
464 cmd = ['python', 'scripts/gyp/finalize_apk.py',
465 '--android-sdk-root=%s' % sdk_root_path,
468 '--keystore-path=%s' % key_store,
469 '--keystore-alias=%s' % key_alias,
470 '--keystore-passcode=%s' % key_code]
473 src_file = os.path.join('out', name + '.apk')
474 package_name = options.name
475 if options.app_version:
476 package_name += ('_' + options.app_version)
477 if options.mode == 'shared':
478 dst_file = os.path.join(options.target_dir, '%s.apk' % package_name)
479 elif options.mode == 'embedded':
480 dst_file = os.path.join(options.target_dir,
481 '%s_%s.apk' % (package_name, options.arch))
482 shutil.copyfile(src_file, dst_file)
484 if options.mode == 'embedded':
485 os.remove(pak_des_path)
488 def PrintPackageInfo(options, packaged_archs):
489 package_name_version = os.path.join(options.target_dir, options.name)
490 if options.app_version:
491 package_name_version += '_' + options.app_version
493 if len(packaged_archs) == 0:
494 print ('A non-platform specific APK for the web application "%s" was '
495 'generated successfully at\n%s.apk. It requires a shared Crosswalk '
496 'Runtime to be present.'
497 % (options.name, package_name_version))
500 for arch in packaged_archs:
501 print ('An APK for the web application "%s" including the Crosswalk '
502 'Runtime built for %s was generated successfully, which can be '
503 'found at\n%s_%s.apk.'
504 % (options.name, arch, package_name_version, arch))
506 all_archs = set(AllArchitectures())
508 if len(packaged_archs) != len(all_archs):
509 missed_archs = all_archs - set(packaged_archs)
510 print ('\n\nWARNING: ')
511 print ('This APK will only work on %s based Android devices. Consider '
512 'building for %s as well.' %
513 (', '.join(packaged_archs), ', '.join(missed_archs)))
515 print ('\n\n%d APKs were created for %s devices. '
516 % (len(all_archs), ', '.join(all_archs)))
517 print ('Please install the one that matches the processor architecture '
518 'of your device.\n\n')
519 print ('If you are going to submit this application to an application '
520 'store, please make sure you submit both packages.\nInstructions '
521 'for submitting multiple APKs to Google Play Store are available '
522 'here:\nhttps://software.intel.com/en-us/html5/articles/submitting'
523 '-multiple-crosswalk-apk-to-google-play-store')
525 def MakeApk(options, app_info):
526 Customize(options, app_info)
529 if options.mode == 'shared':
530 Execution(options, name)
531 elif options.mode == 'embedded':
533 Execution(options, name)
534 packaged_archs.append(options.arch)
536 # If the arch option is unspecified, all of available platform APKs
538 valid_archs = ['x86', 'armeabi-v7a']
539 for arch in valid_archs:
540 lib_path = os.path.join('native_libs', arch, 'libs',
541 arch, 'libxwalkcore.so')
542 if os.path.isfile(lib_path):
543 if arch.find('x86') != -1:
545 elif arch.find('arm') != -1:
547 Execution(options, name)
548 packaged_archs.append(options.arch)
550 print('Warning: failed to create package for arch "%s" '
551 'due to missing library %s' %
554 if len(packaged_archs) == 0:
555 print('No packages created, aborting')
558 print('Unknown mode for packaging the application. Abort!')
561 PrintPackageInfo(options, packaged_archs)
564 parser = optparse.OptionParser()
565 parser.add_option('-v', '--version', action='store_true',
566 dest='version', default=False,
567 help='The version of this python tool.')
568 parser.add_option('--verbose', action="store_true",
569 dest='verbose', default=False,
570 help='Print debug messages.')
571 info = ('The packaging mode of the web application. The value \'shared\' '
572 'means that the runtime is shared across multiple application '
573 'instances and that the runtime needs to be distributed separately. '
574 'The value \'embedded\' means that the runtime is embedded into the '
575 'application itself and distributed along with it.'
576 'Set the default mode as \'embedded\'. For example: --mode=embedded')
577 parser.add_option('--mode', default='embedded', help=info)
578 info = ('The target architecture of the embedded runtime. Supported values '
579 'are \'x86\' and \'arm\'. Note, if undefined, APKs for all possible '
580 'architestures will be generated.')
581 parser.add_option('--arch', choices=AllArchitectures(), help=info)
582 group = optparse.OptionGroup(parser, 'Application Source Options',
583 'This packaging tool supports 3 kinds of web application source: '
584 '1) XPK package; 2) manifest.json; 3) various command line options, '
585 'for example, \'--app-url\' for website, \'--app-root\' and '
586 '\'--app-local-path\' for local web application.')
587 info = ('The path of the XPK package. For example, --xpk=/path/to/xpk/file')
588 group.add_option('--xpk', help=info)
589 info = ('The manifest file with the detail description of the application. '
590 'For example, --manifest=/path/to/your/manifest/file')
591 group.add_option('--manifest', help=info)
592 info = ('The url of application. '
593 'This flag allows to package website as apk. For example, '
594 '--app-url=http://www.intel.com')
595 group.add_option('--app-url', help=info)
596 info = ('The root path of the web app. '
597 'This flag allows to package local web app as apk. For example, '
598 '--app-root=/root/path/of/the/web/app')
599 group.add_option('--app-root', help=info)
600 info = ('The relative path of entry file based on the value from '
601 '\'app_root\'. This flag should work with \'--app-root\' together. '
602 'For example, --app-local-path=/relative/path/of/entry/file')
603 group.add_option('--app-local-path', help=info)
604 parser.add_option_group(group)
605 group = optparse.OptionGroup(parser, 'Mandatory arguments',
606 'They are used for describing the APK information through '
607 'command line options.')
608 info = ('The apk name. For example, --name="Your Application Name"')
609 group.add_option('--name', help=info)
610 info = ('The package name. For example, '
611 '--package=com.example.YourPackage')
612 group.add_option('--package', help=info)
613 parser.add_option_group(group)
614 group = optparse.OptionGroup(parser, 'Optional arguments',
615 'They are used for various settings for applications through '
616 'command line options.')
617 info = ('The version name of the application. '
618 'For example, --app-version=1.0.0')
619 group.add_option('--app-version', help=info)
620 info = ('The version code of the application. '
621 'For example, --app-versionCode=24')
622 group.add_option('--app-versionCode', type='int', help=info)
623 info = ('The version code base of the application. Version code will '
624 'be made by adding a prefix based on architecture to the version '
625 'code base. For example, --app-versionCodeBase=24')
626 group.add_option('--app-versionCodeBase', type='int', help=info)
627 info = ('Use command lines.'
628 'Crosswalk is powered by Chromium and supports Chromium command line.'
630 '--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
631 group.add_option('--xwalk-command-line', default='', help=info)
632 info = ('The description of the application. For example, '
633 '--description=YourApplicationDescription')
634 group.add_option('--description', help=info)
635 group.add_option('--enable-remote-debugging', action='store_true',
636 dest='enable_remote_debugging', default=False,
637 help='Enable remote debugging.')
638 info = ('The list of external extension paths splitted by OS separators. '
639 'The separators are \':\' , \';\' and \':\' on Linux, Windows and '
640 'Mac OS respectively. For example, '
641 '--extensions=/path/to/extension1:/path/to/extension2.')
642 group.add_option('--extensions', help=info)
643 group.add_option('-f', '--fullscreen', action='store_true',
644 dest='fullscreen', default=False,
645 help='Make application fullscreen.')
646 group.add_option('--keep-screen-on', action='store_true', default=False,
647 help='Support keeping screen on')
648 info = ('The path of application icon. '
649 'Such as: --icon=/path/to/your/customized/icon')
650 group.add_option('--icon', help=info)
651 info = ('The orientation of the web app\'s display on the device. '
652 'For example, --orientation=landscape. The default value is '
653 '\'unspecified\'. The permitted values are from Android: '
654 'http://developer.android.com/guide/topics/manifest/'
655 'activity-element.html#screen')
656 group.add_option('--orientation', help=info)
657 info = ('The list of permissions to be used by web application. For example, '
658 '--permissions=geolocation:webgl')
659 group.add_option('--permissions', help=info)
660 info = ('Packaging tool will move the output APKS to the target directory')
661 group.add_option('--target-dir', default=os.getcwd(), help=info)
662 parser.add_option_group(group)
663 group = optparse.OptionGroup(parser, 'Keystore Options',
664 'The keystore is a signature from web developer, it\'s used when '
665 'developer wants to distribute the applications.')
666 info = ('The path to the developer keystore. For example, '
667 '--keystore-path=/path/to/your/developer/keystore')
668 group.add_option('--keystore-path', help=info)
669 info = ('The alias name of keystore. For example, --keystore-alias=name')
670 group.add_option('--keystore-alias', help=info)
671 info = ('The passcode of keystore. For example, --keystore-passcode=code')
672 group.add_option('--keystore-passcode', help=info)
673 info = ('Minify and obfuscate javascript and css.'
674 '--compressor: compress javascript and css.'
675 '--compressor=js: compress javascript.'
676 '--compressor=css: compress css.')
677 group.add_option('--compressor', dest='compressor', action='callback',
678 callback=ParseParameterForCompressor, type='string',
680 parser.add_option_group(group)
681 options, _ = parser.parse_args()
687 if os.path.isfile('VERSION'):
688 print(GetVersion('VERSION'))
691 parser.error('Can\'t get version due to the VERSION file missing!')
695 xpk_name = os.path.splitext(os.path.basename(options.xpk))[0]
696 xpk_temp_dir = xpk_name + '_xpk'
697 ParseXPK(options, xpk_temp_dir)
699 if options.app_root and not options.manifest:
700 manifest_path = os.path.join(options.app_root, 'manifest.json')
701 if os.path.exists(manifest_path):
702 print('Using manifest.json distributed with the application.')
703 options.manifest = manifest_path
706 if not options.manifest:
708 VerifyAppName(options.package, 'packagename')
710 parser.error('The package name is required! '
711 'Please use "--package" option.')
713 VerifyAppName(options.name)
714 app_info.original_name = options.name
715 options.name = ReplaceSpaceWithUnderscore(options.name)
717 parser.error('The APK name is required! Please use "--name" option.')
718 if not ((options.app_url and
719 not options.app_root and
720 not options.app_local_path) or
721 (not options.app_url and
723 options.app_local_path)):
724 parser.error('The entry is required. If the entry is a remote url, '
725 'please use "--app-url" option; If the entry is local, '
726 'please use "--app-root" and '
727 '"--app-local-path" options together!')
728 if options.permissions:
729 permission_list = options.permissions.split(':')
731 print('Warning: all supported permissions on Android port are added. '
732 'Refer to https://github.com/crosswalk-project/'
733 'crosswalk-website/wiki/Crosswalk-manifest')
734 permission_list = permission_mapping_table.keys()
735 options.permissions = HandlePermissionList(permission_list)
736 options.icon_dict = {}
739 ParseManifest(options, app_info)
740 except SystemExit as ec:
743 if (options.app_root and options.app_local_path and
744 not os.path.isfile(os.path.join(options.app_root,
745 options.app_local_path))):
746 print('Please make sure that the local path file of launching app '
750 if options.target_dir:
751 target_dir = os.path.abspath(os.path.expanduser(options.target_dir))
752 options.target_dir = target_dir
753 if not os.path.isdir(target_dir):
754 os.makedirs(target_dir)
757 MakeApk(options, app_info)
758 except SystemExit as ec:
759 CleanDir(options.name)
761 CleanDir(xpk_temp_dir)
766 if __name__ == '__main__':
767 sys.exit(main(sys.argv))