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 VerifyPackageName, CustomizeAll, \
20 ParseParameterForCompressor
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):
32 def AllArchitectures():
36 def RunCommand(command, verbose=False, shell=False):
37 """Runs the command list, print the output, and propagate its result."""
38 proc = subprocess.Popen(command, stdout=subprocess.PIPE,
39 stderr=subprocess.STDOUT, shell=shell)
41 output = proc.communicate()[0]
42 result = proc.returncode
44 print(output.decode("utf-8").strip())
46 print ('Command "%s" exited with non-zero exit code %d'
47 % (' '.join(command), result))
52 """Searches PATH for executable files with the given name, also taking
53 PATHEXT into account. Returns the first existing match, or None if no matches
55 for path in os.environ.get('PATH', '').split(os.pathsep):
56 for filename in AddExeExtensions(name):
57 full_path = os.path.join(path, filename)
58 if os.path.isfile(full_path) and os.access(full_path, os.X_OK):
64 """Find executable file with the given name
65 and maximum API level under specific path."""
67 for root, _, files in os.walk(path):
69 key = os.path.join(root, name)
70 sdk_version = os.path.basename(os.path.dirname(key))
71 str_num = re.search(r'\d+', sdk_version)
73 result[key] = int(str_num.group())
78 return max(iter(result.items()), key=operator.itemgetter(1))[0]
82 """Get the version of this python tool."""
83 version_str = 'Crosswalk app packaging tool version is '
84 file_handle = open(path, 'r')
85 src_content = file_handle.read()
86 version_nums = re.findall(r'\d+', src_content)
87 version_str += ('.').join(version_nums)
92 def ParseManifest(options):
93 parser = ManifestJsonParser(os.path.expanduser(options.manifest))
95 options.name = parser.GetAppName()
96 if not options.app_version:
97 options.app_version = parser.GetVersion()
98 if not options.app_versionCode and not options.app_versionCodeBase:
99 options.app_versionCode = 1
100 if parser.GetDescription():
101 options.description = parser.GetDescription()
102 if parser.GetPermissions():
103 options.permissions = parser.GetPermissions()
104 if parser.GetAppUrl():
105 options.app_url = parser.GetAppUrl()
106 elif parser.GetAppLocalPath():
107 options.app_local_path = parser.GetAppLocalPath()
109 print('Error: there is no app launch path defined in manifest.json.')
111 if parser.GetAppRoot():
112 options.app_root = parser.GetAppRoot()
113 options.icon_dict = parser.GetIcons()
114 if parser.GetOrientation():
115 options.orientation = parser.GetOrientation()
116 if parser.GetFullScreenFlag().lower() == 'true':
117 options.fullscreen = True
118 elif parser.GetFullScreenFlag().lower() == 'false':
119 options.fullscreen = False
123 def ParseXPK(options, out_dir):
124 cmd = ['python', 'parse_xpk.py',
125 '--file=%s' % os.path.expanduser(options.xpk),
126 '--out=%s' % out_dir]
129 print ('Use the manifest from XPK by default '
130 'when "--xpk" option is specified, and '
131 'the "--manifest" option would be ignored.')
134 if os.path.isfile(os.path.join(out_dir, 'manifest.json')):
135 options.manifest = os.path.join(out_dir, 'manifest.json')
137 print('XPK doesn\'t contain manifest file.')
141 def FindExtensionJars(root_path):
142 ''' Find all .jar files for external extensions. '''
144 if not os.path.exists(root_path):
145 return extension_jars
147 for afile in os.listdir(root_path):
148 if os.path.isdir(os.path.join(root_path, afile)):
149 base_name = os.path.basename(afile)
150 extension_jar = os.path.join(root_path, afile, base_name + '.jar')
151 if os.path.isfile(extension_jar):
152 extension_jars.append(extension_jar)
153 return extension_jars
156 # Follows the recommendation from
157 # http://software.intel.com/en-us/blogs/2012/11/12/how-to-publish-
158 # your-apps-on-google-play-for-x86-based-android-devices-using
159 def MakeVersionCode(options):
160 ''' Construct a version code'''
161 if options.app_versionCode:
162 return options.app_versionCode
164 # First digit is ABI, ARM=2, x86=6
166 if options.arch == 'arm':
168 if options.arch == 'x86':
171 if options.app_versionCodeBase:
172 b = str(options.app_versionCodeBase)
174 print('Version code base must be 7 digits or less: '
175 'versionCodeBase=%s' % (b))
177 # zero pad to 7 digits, middle digits can be used for other
178 # features, according to recommendation in URL
179 return '%s%s' % (abi, b.zfill(7))
182 def Customize(options, app_info, manifest):
183 app_info.package = options.package
184 app_info.app_name = options.name
185 # 'org.xwalk.my_first_app' => 'MyFirstApp'
186 android_name = options.package.split('.')[-1].split('_')
187 app_info.android_name = ''.join([i.capitalize() for i in android_name if i])
188 if options.app_version:
189 app_info.app_version = options.app_version
190 app_info.app_versionCode = MakeVersionCode(options)
192 app_info.app_root = os.path.expanduser(options.app_root)
193 if options.enable_remote_debugging:
194 app_info.remote_debugging = '--enable-remote-debugging'
195 if options.fullscreen:
196 app_info.fullscreen_flag = '-f'
197 if options.orientation:
198 app_info.orientation = options.orientation
200 app_info.icon = '%s' % os.path.expanduser(options.icon)
201 CustomizeAll(app_info, options.description, options.icon_dict,
202 options.permissions, options.app_url, options.app_local_path,
203 options.keep_screen_on, options.extensions, manifest,
204 options.xwalk_command_line, options.compressor)
207 def Execution(options, name):
208 android_path = Which('android')
209 if android_path is None:
210 print('The "android" binary could not be found. Check your Android SDK '
211 'installation and your PATH environment variable.')
214 sdk_root_path = os.path.dirname(os.path.dirname(android_path))
217 sdk_jar_path = Find('android.jar',
218 os.path.join(sdk_root_path, 'platforms'))
220 print('Your Android SDK may be ruined, please reinstall it.')
223 level_string = os.path.basename(os.path.dirname(sdk_jar_path))
224 api_level = int(re.search(r'\d+', level_string).group())
226 print('Please install Android API level (>=14) first.')
229 if options.keystore_path:
230 key_store = os.path.expanduser(options.keystore_path)
231 if options.keystore_alias:
232 key_alias = options.keystore_alias
234 print('Please provide an alias name of the developer key.')
236 if options.keystore_passcode:
237 key_code = options.keystore_passcode
239 print('Please provide the passcode of the developer key.')
241 if options.keystore_alias_passcode:
242 key_alias_code = options.keystore_alias_passcode
244 print('--keystore-alias-passcode was not specified, '
245 'using the keystore\'s passcode as the alias keystore passcode.')
246 key_alias_code = key_code
248 print ('Use xwalk\'s keystore by default for debugging. '
249 'Please switch to your keystore when distributing it to app market.')
250 key_store = 'scripts/ant/xwalk-debug.keystore'
251 key_alias = 'xwalkdebugkey'
252 key_code = 'xwalkdebug'
253 key_alias_code = 'xwalkdebug'
255 if not os.path.exists('out'):
258 # Make sure to use ant-tasks.jar correctly.
259 # Default Android SDK names it as ant-tasks.jar
260 # Chrome third party Android SDk names it as anttasks.jar
261 ant_tasks_jar_path = os.path.join(sdk_root_path,
262 'tools', 'lib', 'ant-tasks.jar')
263 if not os.path.exists(ant_tasks_jar_path):
264 ant_tasks_jar_path = os.path.join(sdk_root_path,
265 'tools', 'lib', 'anttasks.jar')
268 for aapt_str in AddExeExtensions('aapt'):
270 aapt_path = Find(aapt_str, sdk_root_path)
271 print('Use %s in %s.' % (aapt_str, sdk_root_path))
276 print('Your Android SDK may be ruined, please reinstall it.')
279 # Check whether ant is installed.
281 cmd = ['ant', '-version']
282 RunCommand(cmd, shell=True)
283 except EnvironmentError:
284 print('Please install ant first.')
287 res_dirs = '-DADDITIONAL_RES_DIRS=\'\''
288 res_packages = '-DADDITIONAL_RES_PACKAGES=\'\''
289 res_r_text_files = '-DADDITIONAL_R_TEXT_FILES=\'\''
290 if options.mode == 'embedded':
291 # Prepare the .pak file for embedded mode.
292 pak_src_path = os.path.join('native_libs_res', 'xwalk.pak')
293 pak_des_path = os.path.join(name, 'assets', 'xwalk.pak')
294 shutil.copy(pak_src_path, pak_des_path)
296 # Prepare the icudtl.dat for embedded mode.
297 icudtl_src_path = os.path.join('native_libs_res', 'icudtl.dat')
298 icudtl_des_path = os.path.join(name, 'assets', 'icudtl.dat')
299 shutil.copy(icudtl_src_path, icudtl_des_path)
301 js_src_dir = os.path.join('native_libs_res', 'jsapi')
302 js_des_dir = os.path.join(name, 'assets', 'jsapi')
303 if os.path.exists(js_des_dir):
304 shutil.rmtree(js_des_dir)
305 shutil.copytree(js_src_dir, js_des_dir)
307 res_ui_java = os.path.join('gen', 'ui_java')
308 res_content_java = os.path.join('gen', 'content_java')
309 res_xwalk_java = os.path.join('gen', 'xwalk_core_internal_java')
310 res_dir = os.path.join('libs_res', 'xwalk_core_library')
311 res_dirs = ('-DADDITIONAL_RES_DIRS=' + res_dir)
312 res_packages = ('-DADDITIONAL_RES_PACKAGES=org.chromium.ui '
313 'org.xwalk.core.internal org.chromium.content')
314 res_r_text_files = ('-DADDITIONAL_R_TEXT_FILES='
315 + os.path.join(res_ui_java, 'java_R', 'R.txt') + ' '
316 + os.path.join(res_xwalk_java, 'java_R', 'R.txt') + ' '
317 + os.path.join(res_content_java, 'java_R', 'R.txt'))
319 resource_dir = '-DRESOURCE_DIR=' + os.path.join(name, 'res')
320 manifest_path = os.path.join(name, 'AndroidManifest.xml')
321 cmd = ['python', os.path.join('scripts', 'gyp', 'ant.py'),
322 '-DAAPT_PATH=%s' % aapt_path,
326 '-DANDROID_MANIFEST=%s' % manifest_path,
327 '-DANDROID_SDK_JAR=%s' % sdk_jar_path,
328 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
329 '-DANDROID_SDK_VERSION=%d' % api_level,
330 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
331 '-DLIBRARY_MANIFEST_PATHS= ',
334 '-DSTAMP=codegen.stamp',
337 os.path.join('scripts', 'ant', 'apk-codegen.xml')]
338 RunCommand(cmd, options.verbose)
340 # Check whether java is installed.
342 cmd = ['java', '-version']
343 RunCommand(cmd, shell=True)
344 except EnvironmentError:
345 print('Please install Oracle JDK first.')
348 # Compile App source code with app runtime code.
349 cmd = ['python', os.path.join('scripts', 'gyp', 'javac.py'),
350 '--output-dir=%s' % os.path.join('out', 'classes'),
352 os.path.join(os.getcwd(), 'libs', 'xwalk_app_runtime_java.jar'),
356 os.path.join(os.getcwd(), name, 'src'),
358 os.path.join(os.getcwd(), 'out', 'gen'),
360 '--stamp=compile.stam']
361 RunCommand(cmd, options.verbose)
364 asset_dir = '-DASSET_DIR=%s' % os.path.join(name, 'assets')
365 xml_path = os.path.join('scripts', 'ant', 'apk-package-resources.xml')
366 cmd = ['python', os.path.join('scripts', 'gyp', 'ant.py'),
367 '-DAAPT_PATH=%s' % aapt_path,
371 '-DANDROID_SDK_JAR=%s' % sdk_jar_path,
372 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
373 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
374 '-DAPK_NAME=%s' % name,
375 '-DAPP_MANIFEST_VERSION_CODE=0',
376 '-DAPP_MANIFEST_VERSION_NAME=Developer Build',
378 '-DCONFIGURATION_NAME=Release',
381 '-DSTAMP=package_resources.stamp',
385 RunCommand(cmd, options.verbose)
387 dex_path = '--dex-path=' + os.path.join(os.getcwd(), 'out', 'classes.dex')
388 app_runtime_jar = os.path.join(os.getcwd(),
389 'libs', 'xwalk_app_runtime_java.jar')
391 # Check whether external extensions are included.
392 extensions_string = 'xwalk-extensions'
393 extensions_dir = os.path.join(os.getcwd(), name, extensions_string)
394 external_extension_jars = FindExtensionJars(extensions_dir)
396 if options.mode == 'embedded':
397 input_jars.append(os.path.join(os.getcwd(), 'libs',
398 'xwalk_runtime_embedded.dex.jar'))
399 dex_command_list = ['python', os.path.join('scripts', 'gyp', 'dex.py'),
401 '--android-sdk-root=%s' % sdk_root_path,
403 os.path.join(os.getcwd(), 'out', 'classes')]
404 dex_command_list.extend(external_extension_jars)
405 dex_command_list.extend(input_jars)
406 RunCommand(dex_command_list)
408 src_dir = '-DSOURCE_DIR=' + os.path.join(name, 'src')
409 apk_path = '-DUNSIGNED_APK_PATH=' + os.path.join('out', 'app-unsigned.apk')
410 native_lib_path = '-DNATIVE_LIBS_DIR='
411 if options.mode == 'embedded':
412 if options.arch == 'x86':
413 x86_native_lib_path = os.path.join('native_libs', 'x86', 'libs',
414 'x86', 'libxwalkcore.so')
415 if os.path.isfile(x86_native_lib_path):
416 native_lib_path += os.path.join('native_libs', 'x86', 'libs')
418 print('No x86 native library has been found for creating a Crosswalk '
421 elif options.arch == 'arm':
422 arm_native_lib_path = os.path.join('native_libs', 'armeabi-v7a', 'libs',
423 'armeabi-v7a', 'libxwalkcore.so')
424 if os.path.isfile(arm_native_lib_path):
425 native_lib_path += os.path.join('native_libs', 'armeabi-v7a', 'libs')
427 print('No ARM native library has been found for creating a Crosswalk '
430 # A space is needed for Windows.
431 native_lib_path += ' '
432 cmd = ['python', 'scripts/gyp/ant.py',
433 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
434 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
435 '-DAPK_NAME=%s' % name,
436 '-DCONFIGURATION_NAME=Release',
443 'scripts/ant/apk-package.xml']
444 RunCommand(cmd, options.verbose)
446 # Find the path of zipalign.
447 # XWALK-2033: zipalign can be in different locations depending on Android
448 # SDK version that used ((eg. /tools, /build-tools/android-4.4W etc),).
449 # So looking up the location of zipalign here instead of hard coding.
450 # Refer to: https://codereview.chromium.org/238253015
452 for zipalign_str in AddExeExtensions('zipalign'):
454 zipalign_path = Find(zipalign_str, sdk_root_path)
456 print('Use %s in %s.' % (zipalign_str, sdk_root_path))
460 if not zipalign_path:
461 print('zipalign could not be found in your Android SDK.'
462 ' Make sure it is installed.')
464 apk_path = '--unsigned-apk-path=' + os.path.join('out', 'app-unsigned.apk')
465 final_apk_path = '--final-apk-path=' + \
466 os.path.join('out', name + '.apk')
467 cmd = ['python', 'scripts/gyp/finalize_apk.py',
468 '--zipalign-path=%s' % zipalign_path,
471 '--keystore-path=%s' % key_store,
472 '--keystore-alias=%s' % key_alias,
473 '--keystore-passcode=%s' % key_code,
474 '--keystore-alias-passcode=%s' % key_alias_code]
477 src_file = os.path.join('out', name + '.apk')
479 if options.app_version:
480 package_name += ('_' + options.app_version)
481 if options.mode == 'shared':
482 dst_file = os.path.join(options.target_dir, '%s.apk' % package_name)
483 elif options.mode == 'embedded':
484 dst_file = os.path.join(options.target_dir,
485 '%s_%s.apk' % (package_name, options.arch))
486 shutil.copyfile(src_file, dst_file)
488 if options.mode == 'embedded':
489 os.remove(pak_des_path)
492 def PrintPackageInfo(options, name, packaged_archs):
493 package_name_version = os.path.join(options.target_dir, name)
494 if options.app_version:
495 package_name_version += '_' + options.app_version
497 if len(packaged_archs) == 0:
498 print ('A non-platform specific APK for the web application "%s" was '
499 'generated successfully at\n%s.apk. It requires a shared Crosswalk '
500 'Runtime to be present.'
501 % (name, package_name_version))
504 for arch in packaged_archs:
505 print ('An APK for the web application "%s" including the Crosswalk '
506 'Runtime built for %s was generated successfully, which can be '
507 'found at\n%s_%s.apk.'
508 % (name, arch, package_name_version, arch))
510 all_archs = set(AllArchitectures())
512 if len(packaged_archs) != len(all_archs):
513 missed_archs = all_archs - set(packaged_archs)
514 print ('\n\nWARNING: ')
515 print ('This APK will only work on %s based Android devices. Consider '
516 'building for %s as well.' %
517 (', '.join(packaged_archs), ', '.join(missed_archs)))
519 print ('\n\n%d APKs were created for %s devices. '
520 % (len(all_archs), ', '.join(all_archs)))
521 print ('Please install the one that matches the processor architecture '
522 'of your device.\n\n')
523 print ('If you are going to submit this application to an application '
524 'store, please make sure you submit both packages.\nInstructions '
525 'for submitting multiple APKs to Google Play Store are available '
526 'here:\nhttps://software.intel.com/en-us/html5/articles/submitting'
527 '-multiple-crosswalk-apk-to-google-play-store')
529 def MakeApk(options, app_info, manifest):
530 Customize(options, app_info, manifest)
531 name = app_info.android_name
533 if options.mode == 'shared':
534 Execution(options, name)
535 elif options.mode == 'embedded':
537 Execution(options, name)
538 packaged_archs.append(options.arch)
540 # If the arch option is unspecified, all of available platform APKs
542 valid_archs = ['x86', 'armeabi-v7a']
543 for arch in valid_archs:
544 lib_path = os.path.join('native_libs', arch, 'libs',
545 arch, 'libxwalkcore.so')
546 if os.path.isfile(lib_path):
547 if arch.find('x86') != -1:
549 elif arch.find('arm') != -1:
551 Execution(options, name)
552 packaged_archs.append(options.arch)
554 print('Warning: failed to create package for arch "%s" '
555 'due to missing library %s' %
558 if len(packaged_archs) == 0:
559 print('No packages created, aborting')
562 PrintPackageInfo(options, name, packaged_archs)
565 parser = optparse.OptionParser()
566 parser.add_option('-v', '--version', action='store_true',
567 dest='version', default=False,
568 help='The version of this python tool.')
569 parser.add_option('--verbose', action="store_true",
570 dest='verbose', default=False,
571 help='Print debug messages.')
572 info = ('The packaging mode of the web application. The value \'shared\' '
573 'means that the runtime is shared across multiple application '
574 'instances and that the runtime needs to be distributed separately. '
575 'The value \'embedded\' means that the runtime is embedded into the '
576 'application itself and distributed along with it.'
577 'Set the default mode as \'embedded\'. For example: --mode=embedded')
578 parser.add_option('--mode', choices=('embedded', 'shared'),
579 default='embedded', help=info)
580 info = ('The target architecture of the embedded runtime. Supported values '
581 'are \'x86\' and \'arm\'. Note, if undefined, APKs for all possible '
582 'architestures will be generated.')
583 parser.add_option('--arch', choices=AllArchitectures(), help=info)
584 group = optparse.OptionGroup(parser, 'Application Source Options',
585 'This packaging tool supports 3 kinds of web application source: '
586 '1) XPK package; 2) manifest.json; 3) various command line options, '
587 'for example, \'--app-url\' for website, \'--app-root\' and '
588 '\'--app-local-path\' for local web application.')
589 info = ('The path of the XPK package. For example, --xpk=/path/to/xpk/file')
590 group.add_option('--xpk', help=info)
591 info = ('The manifest file with the detail description of the application. '
592 'For example, --manifest=/path/to/your/manifest/file')
593 group.add_option('--manifest', help=info)
594 info = ('The url of application. '
595 'This flag allows to package website as apk. For example, '
596 '--app-url=http://www.intel.com')
597 group.add_option('--app-url', help=info)
598 info = ('The root path of the web app. '
599 'This flag allows to package local web app as apk. For example, '
600 '--app-root=/root/path/of/the/web/app')
601 group.add_option('--app-root', help=info)
602 info = ('The relative path of entry file based on the value from '
603 '\'app_root\'. This flag should work with \'--app-root\' together. '
604 'For example, --app-local-path=/relative/path/of/entry/file')
605 group.add_option('--app-local-path', help=info)
606 parser.add_option_group(group)
607 group = optparse.OptionGroup(parser, 'Mandatory arguments',
608 'They are used for describing the APK information through '
609 'command line options.')
610 info = ('The apk name. For example, --name="Your Application Name"')
611 group.add_option('--name', help=info)
612 info = ('The package name. For example, '
613 '--package=com.example.YourPackage')
614 group.add_option('--package', help=info)
615 parser.add_option_group(group)
616 group = optparse.OptionGroup(parser, 'Optional arguments',
617 'They are used for various settings for applications through '
618 'command line options.')
619 info = ('The version name of the application. '
620 'For example, --app-version=1.0.0')
621 group.add_option('--app-version', help=info)
622 info = ('The version code of the application. '
623 'For example, --app-versionCode=24')
624 group.add_option('--app-versionCode', type='int', help=info)
625 info = ('The version code base of the application. Version code will '
626 'be made by adding a prefix based on architecture to the version '
627 'code base. For example, --app-versionCodeBase=24')
628 group.add_option('--app-versionCodeBase', type='int', help=info)
629 info = ('Use command lines.'
630 'Crosswalk is powered by Chromium and supports Chromium command line.'
632 '--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
633 group.add_option('--xwalk-command-line', default='', help=info)
634 info = ('The description of the application. For example, '
635 '--description=YourApplicationDescription')
636 group.add_option('--description', help=info)
637 group.add_option('--enable-remote-debugging', action='store_true',
638 dest='enable_remote_debugging', default=False,
639 help='Enable remote debugging.')
640 info = ('The list of external extension paths splitted by OS separators. '
641 'The separators are \':\' , \';\' and \':\' on Linux, Windows and '
642 'Mac OS respectively. For example, '
643 '--extensions=/path/to/extension1:/path/to/extension2.')
644 group.add_option('--extensions', help=info)
645 group.add_option('-f', '--fullscreen', action='store_true',
646 dest='fullscreen', default=False,
647 help='Make application fullscreen.')
648 group.add_option('--keep-screen-on', action='store_true', default=False,
649 help='Support keeping screen on')
650 info = ('The path of application icon. '
651 'Such as: --icon=/path/to/your/customized/icon')
652 group.add_option('--icon', help=info)
653 info = ('The orientation of the web app\'s display on the device. '
654 'For example, --orientation=landscape. The default value is '
655 '\'unspecified\'. The permitted values are from Android: '
656 'http://developer.android.com/guide/topics/manifest/'
657 'activity-element.html#screen')
658 group.add_option('--orientation', help=info)
659 info = ('The list of permissions to be used by web application. For example, '
660 '--permissions=geolocation:webgl')
661 group.add_option('--permissions', help=info)
662 info = ('Packaging tool will move the output APKS to the target directory')
663 group.add_option('--target-dir', default=os.getcwd(), help=info)
664 parser.add_option_group(group)
665 group = optparse.OptionGroup(parser, 'Keystore Options',
666 'The keystore is a signature from web developer, it\'s used when '
667 'developer wants to distribute the applications.')
668 info = ('The path to the developer keystore. For example, '
669 '--keystore-path=/path/to/your/developer/keystore')
670 group.add_option('--keystore-path', help=info)
671 info = ('The alias name of keystore. For example, --keystore-alias=name')
672 group.add_option('--keystore-alias', help=info)
673 info = ('The passcode of keystore. For example, --keystore-passcode=code')
674 group.add_option('--keystore-passcode', help=info)
675 info = ('Passcode for alias\'s private key in the keystore, '
676 'For example, --keystore-alias-passcode=alias-code')
677 group.add_option('--keystore-alias-passcode', help=info)
678 info = ('Minify and obfuscate javascript and css.'
679 '--compressor: compress javascript and css.'
680 '--compressor=js: compress javascript.'
681 '--compressor=css: compress css.')
682 group.add_option('--compressor', dest='compressor', action='callback',
683 callback=ParseParameterForCompressor, type='string',
685 parser.add_option_group(group)
686 options, _ = parser.parse_args()
692 if os.path.isfile('VERSION'):
693 print(GetVersion('VERSION'))
696 parser.error('VERSION was not found, so Crosswalk\'s version could not '
701 xpk_name = os.path.splitext(os.path.basename(options.xpk))[0]
702 xpk_temp_dir = xpk_name + '_xpk'
703 ParseXPK(options, xpk_temp_dir)
705 if options.app_root and not options.manifest:
706 manifest_path = os.path.join(options.app_root, 'manifest.json')
707 if os.path.exists(manifest_path):
708 print('Using manifest.json distributed with the application.')
709 options.manifest = manifest_path
713 if not options.manifest:
714 # The checks here are really convoluted, but at the moment make_apk
715 # misbehaves any of the following conditions is true.
717 # 1) --app-url must be passed without either --app-local-path or
719 if options.app_root or options.app_local_path:
720 parser.error('You must pass either "--app-url" or "--app-local-path" '
721 'with "--app-root", but not all.')
723 # 2) --app-url is not passed but only one of --app-local-path and
725 if bool(options.app_root) != bool(options.app_local_path):
726 parser.error('You must specify both "--app-local-path" and '
728 # 3) None of --app-url, --app-local-path and --app-root are passed.
729 elif not options.app_root and not options.app_local_path:
730 parser.error('You must pass either "--app-url" or "--app-local-path" '
731 'with "--app-root".')
733 if options.permissions:
734 permission_list = options.permissions.split(':')
736 print('Warning: all supported permissions on Android port are added. '
737 'Refer to https://github.com/crosswalk-project/'
738 'crosswalk-website/wiki/Crosswalk-manifest')
739 permission_list = permission_mapping_table.keys()
740 options.permissions = HandlePermissionList(permission_list)
741 options.icon_dict = {}
744 manifest = ParseManifest(options)
745 except SystemExit as ec:
749 parser.error('An APK name is required. Please use the "--name" option.')
751 if not options.package:
752 parser.error('A package name is required. Please use the "--package" '
754 VerifyPackageName(options.package)
756 if (options.app_root and options.app_local_path and
757 not os.path.isfile(os.path.join(options.app_root,
758 options.app_local_path))):
759 print('Please make sure that the local path file of launching app '
763 if options.target_dir:
764 target_dir = os.path.abspath(os.path.expanduser(options.target_dir))
765 options.target_dir = target_dir
766 if not os.path.isdir(target_dir):
767 os.makedirs(target_dir)
770 MakeApk(options, app_info, manifest)
771 except SystemExit as ec:
772 CleanDir(app_info.android_name)
774 CleanDir(xpk_temp_dir)
779 if __name__ == '__main__':
780 sys.exit(main(sys.argv))