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):
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, app_info):
93 parser = ManifestJsonParser(os.path.expanduser(options.manifest))
94 original_name = app_info.original_name = parser.GetAppName()
97 VerifyAppName(options.package, 'packagename')
99 VerifyAppName(original_name)
100 app_name = ReplaceSpaceWithUnderscore(original_name)
101 options.package = 'org.xwalk.' + app_name.lower()
103 VerifyAppName(options.name)
104 app_info.original_name = options.name
105 options.name = ReplaceSpaceWithUnderscore(options.name)
107 VerifyAppName(original_name)
108 options.name = ReplaceSpaceWithUnderscore(original_name)
109 if not options.app_version:
110 options.app_version = parser.GetVersion()
111 if not options.app_versionCode and not options.app_versionCodeBase:
112 options.app_versionCode = 1
113 if parser.GetDescription():
114 options.description = parser.GetDescription()
115 if parser.GetPermissions():
116 options.permissions = parser.GetPermissions()
117 if parser.GetAppUrl():
118 options.app_url = parser.GetAppUrl()
119 elif parser.GetAppLocalPath():
120 options.app_local_path = parser.GetAppLocalPath()
122 print('Error: there is no app launch path defined in manifest.json.')
124 if parser.GetAppRoot():
125 options.app_root = parser.GetAppRoot()
126 options.icon_dict = parser.GetIcons()
127 if parser.GetOrientation():
128 options.orientation = parser.GetOrientation()
129 if parser.GetFullScreenFlag().lower() == 'true':
130 options.fullscreen = True
131 elif parser.GetFullScreenFlag().lower() == 'false':
132 options.fullscreen = False
135 def ParseXPK(options, out_dir):
136 cmd = ['python', 'parse_xpk.py',
137 '--file=%s' % os.path.expanduser(options.xpk),
138 '--out=%s' % out_dir]
141 print ('Use the manifest from XPK by default '
142 'when "--xpk" option is specified, and '
143 'the "--manifest" option would be ignored.')
146 if os.path.isfile(os.path.join(out_dir, 'manifest.json')):
147 options.manifest = os.path.join(out_dir, 'manifest.json')
149 print('XPK doesn\'t contain manifest file.')
153 def FindExtensionJars(root_path):
154 ''' Find all .jar files for external extensions. '''
156 if not os.path.exists(root_path):
157 return extension_jars
159 for afile in os.listdir(root_path):
160 if os.path.isdir(os.path.join(root_path, afile)):
161 base_name = os.path.basename(afile)
162 extension_jar = os.path.join(root_path, afile, base_name + '.jar')
163 if os.path.isfile(extension_jar):
164 extension_jars.append(extension_jar)
165 return extension_jars
168 # Follows the recommendation from
169 # http://software.intel.com/en-us/blogs/2012/11/12/how-to-publish-
170 # your-apps-on-google-play-for-x86-based-android-devices-using
171 def MakeVersionCode(options):
172 ''' Construct a version code'''
173 if options.app_versionCode:
174 return options.app_versionCode
176 # First digit is ABI, ARM=2, x86=6
178 if options.arch == 'arm':
180 if options.arch == 'x86':
183 if options.app_versionCodeBase:
184 b = str(options.app_versionCodeBase)
186 print('Version code base must be 7 digits or less: '
187 'versionCodeBase=%s' % (b))
189 # zero pad to 7 digits, middle digits can be used for other
190 # features, according to recommendation in URL
191 return '%s%s' % (abi, b.zfill(7))
194 def Customize(options, app_info):
196 app_info.package = options.package
198 app_info.name = options.name
199 if options.app_version:
200 app_info.app_version = options.app_version
201 app_info.app_versionCode = MakeVersionCode(options)
203 app_info.app_root = os.path.expanduser(options.app_root)
204 if options.enable_remote_debugging:
205 app_info.remote_debugging = '--enable-remote-debugging'
206 if options.fullscreen:
207 app_info.fullscreen_flag = '-f'
208 if options.orientation:
209 app_info.orientation = options.orientation
211 app_info.icon = '%s' % os.path.expanduser(options.icon)
212 CustomizeAll(app_info, options.description, options.icon_dict,
213 options.permissions, options.app_url, options.app_local_path,
214 options.keep_screen_on, options.extensions, options.manifest,
215 options.xwalk_command_line, options.compressor)
218 def Execution(options, name):
219 android_path = Which('android')
220 if android_path is None:
221 print('The "android" binary could not be found. Check your Android SDK '
222 'installation and your PATH environment variable.')
225 sdk_root_path = os.path.dirname(os.path.dirname(android_path))
228 sdk_jar_path = Find('android.jar',
229 os.path.join(sdk_root_path, 'platforms'))
231 print('Your Android SDK may be ruined, please reinstall it.')
234 level_string = os.path.basename(os.path.dirname(sdk_jar_path))
235 api_level = int(re.search(r'\d+', level_string).group())
237 print('Please install Android API level (>=14) first.')
240 if options.keystore_path:
241 key_store = os.path.expanduser(options.keystore_path)
242 if options.keystore_alias:
243 key_alias = options.keystore_alias
245 print('Please provide an alias name of the developer key.')
247 if options.keystore_passcode:
248 key_code = options.keystore_passcode
250 print('Please provide the passcode of the developer key.')
253 print ('Use xwalk\'s keystore by default for debugging. '
254 'Please switch to your keystore when distributing it to app market.')
255 key_store = 'scripts/ant/xwalk-debug.keystore'
256 key_alias = 'xwalkdebugkey'
257 key_code = 'xwalkdebug'
259 if not os.path.exists('out'):
262 # Make sure to use ant-tasks.jar correctly.
263 # Default Android SDK names it as ant-tasks.jar
264 # Chrome third party Android SDk names it as anttasks.jar
265 ant_tasks_jar_path = os.path.join(sdk_root_path,
266 'tools', 'lib', 'ant-tasks.jar')
267 if not os.path.exists(ant_tasks_jar_path):
268 ant_tasks_jar_path = os.path.join(sdk_root_path,
269 'tools', 'lib', 'anttasks.jar')
272 for aapt_str in AddExeExtensions('aapt'):
274 aapt_path = Find(aapt_str, sdk_root_path)
275 print('Use %s in %s.' % (aapt_str, sdk_root_path))
280 print('Your Android SDK may be ruined, please reinstall it.')
283 # Check whether ant is installed.
285 cmd = ['ant', '-version']
286 RunCommand(cmd, shell=True)
287 except EnvironmentError:
288 print('Please install ant first.')
291 res_dirs = '-DADDITIONAL_RES_DIRS=\'\''
292 res_packages = '-DADDITIONAL_RES_PACKAGES=\'\''
293 res_r_text_files = '-DADDITIONAL_R_TEXT_FILES=\'\''
294 if options.mode == 'embedded':
295 # Prepare the .pak file for embedded mode.
296 pak_src_path = os.path.join('native_libs_res', 'xwalk.pak')
297 pak_des_path = os.path.join(name, 'assets', 'xwalk.pak')
298 shutil.copy(pak_src_path, pak_des_path)
300 # Prepare the icudtl.dat for embedded mode.
301 icudtl_src_path = os.path.join('native_libs_res', 'icudtl.dat')
302 icudtl_des_path = os.path.join(name, 'assets', 'icudtl.dat')
303 shutil.copy(icudtl_src_path, icudtl_des_path)
305 js_src_dir = os.path.join('native_libs_res', 'jsapi')
306 js_des_dir = os.path.join(name, 'assets', 'jsapi')
307 if os.path.exists(js_des_dir):
308 shutil.rmtree(js_des_dir)
309 shutil.copytree(js_src_dir, js_des_dir)
311 res_ui_java = os.path.join('gen', 'ui_java')
312 res_content_java = os.path.join('gen', 'content_java')
313 res_xwalk_java = os.path.join('gen', 'xwalk_core_internal_java')
314 res_dirs = ('-DADDITIONAL_RES_DIRS='
315 + os.path.join(res_ui_java, 'res_crunched') + ' '
316 + os.path.join(res_ui_java, 'res_v14_compatibility') + ' '
317 + os.path.join(res_ui_java, 'res_grit') + ' '
318 + os.path.join('libs_res', 'ui') + ' '
319 + os.path.join(res_content_java, 'res_crunched') + ' '
320 + os.path.join(res_content_java, 'res_v14_compatibility') + ' '
321 + os.path.join('libs_res', 'content') + ' '
322 + os.path.join(res_content_java, 'res_grit') + ' '
323 + os.path.join(res_xwalk_java, 'res_crunched') + ' '
324 + os.path.join(res_xwalk_java, 'res_v14_compatibility') + ' '
325 + os.path.join('libs_res', 'runtime') + ' '
326 + os.path.join(res_xwalk_java, 'res_grit'))
327 res_packages = ('-DADDITIONAL_RES_PACKAGES=org.chromium.ui '
328 'org.xwalk.core.internal org.chromium.content')
329 res_r_text_files = ('-DADDITIONAL_R_TEXT_FILES='
330 + os.path.join(res_ui_java, 'java_R', 'R.txt') + ' '
331 + os.path.join(res_xwalk_java, 'java_R', 'R.txt') + ' '
332 + os.path.join(res_content_java, 'java_R', 'R.txt'))
334 resource_dir = '-DRESOURCE_DIR=' + os.path.join(name, 'res')
335 manifest_path = os.path.join(name, 'AndroidManifest.xml')
336 cmd = ['python', os.path.join('scripts', 'gyp', 'ant.py'),
337 '-DAAPT_PATH=%s' % aapt_path,
341 '-DANDROID_MANIFEST=%s' % manifest_path,
342 '-DANDROID_SDK_JAR=%s' % sdk_jar_path,
343 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
344 '-DANDROID_SDK_VERSION=%d' % api_level,
345 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
346 '-DLIBRARY_MANIFEST_PATHS= ',
349 '-DSTAMP=codegen.stamp',
352 os.path.join('scripts', 'ant', 'apk-codegen.xml')]
353 RunCommand(cmd, options.verbose)
355 # Check whether java is installed.
357 cmd = ['java', '-version']
358 RunCommand(cmd, shell=True)
359 except EnvironmentError:
360 print('Please install Oracle JDK first.')
363 # Compile App source code with app runtime code.
364 cmd = ['python', os.path.join('scripts', 'gyp', 'javac.py'),
365 '--output-dir=%s' % os.path.join('out', 'classes'),
367 os.path.join(os.getcwd(), 'libs', 'xwalk_app_runtime_java.jar'),
371 os.path.join(os.getcwd(), name, 'src'),
373 os.path.join(os.getcwd(), 'out', 'gen'),
375 '--stamp=compile.stam']
376 RunCommand(cmd, options.verbose)
379 asset_dir = '-DASSET_DIR=%s' % os.path.join(name, 'assets')
380 xml_path = os.path.join('scripts', 'ant', 'apk-package-resources.xml')
381 cmd = ['python', os.path.join('scripts', 'gyp', 'ant.py'),
382 '-DAAPT_PATH=%s' % aapt_path,
386 '-DANDROID_SDK_JAR=%s' % sdk_jar_path,
387 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
388 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
389 '-DAPK_NAME=%s' % name,
390 '-DAPP_MANIFEST_VERSION_CODE=0',
391 '-DAPP_MANIFEST_VERSION_NAME=Developer Build',
393 '-DCONFIGURATION_NAME=Release',
396 '-DSTAMP=package_resources.stamp',
400 RunCommand(cmd, options.verbose)
402 dex_path = '--dex-path=' + os.path.join(os.getcwd(), 'out', 'classes.dex')
403 app_runtime_jar = os.path.join(os.getcwd(),
404 'libs', 'xwalk_app_runtime_java.jar')
406 # Check whether external extensions are included.
407 extensions_string = 'xwalk-extensions'
408 extensions_dir = os.path.join(os.getcwd(), name, extensions_string)
409 external_extension_jars = FindExtensionJars(extensions_dir)
411 if options.mode == 'embedded':
412 input_jars.append(os.path.join(os.getcwd(), 'libs',
413 'xwalk_runtime_embedded.dex.jar'))
414 dex_command_list = ['python', os.path.join('scripts', 'gyp', 'dex.py'),
416 '--android-sdk-root=%s' % sdk_root_path,
418 os.path.join(os.getcwd(), 'out', 'classes')]
419 dex_command_list.extend(external_extension_jars)
420 dex_command_list.extend(input_jars)
421 RunCommand(dex_command_list)
423 src_dir = '-DSOURCE_DIR=' + os.path.join(name, 'src')
424 apk_path = '-DUNSIGNED_APK_PATH=' + os.path.join('out', 'app-unsigned.apk')
425 native_lib_path = '-DNATIVE_LIBS_DIR='
426 if options.mode == 'embedded':
427 if options.arch == 'x86':
428 x86_native_lib_path = os.path.join('native_libs', 'x86', 'libs',
429 'x86', 'libxwalkcore.so')
430 if os.path.isfile(x86_native_lib_path):
431 native_lib_path += os.path.join('native_libs', 'x86', 'libs')
433 print('No x86 native library has been found for creating a Crosswalk '
436 elif options.arch == 'arm':
437 arm_native_lib_path = os.path.join('native_libs', 'armeabi-v7a', 'libs',
438 'armeabi-v7a', 'libxwalkcore.so')
439 if os.path.isfile(arm_native_lib_path):
440 native_lib_path += os.path.join('native_libs', 'armeabi-v7a', 'libs')
442 print('No ARM native library has been found for creating a Crosswalk '
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 # Find the path of zipalign.
462 # XWALK-2033: zipalign can be in different locations depending on Android
463 # SDK version that used ((eg. /tools, /build-tools/android-4.4W etc),).
464 # So looking up the location of zipalign here instead of hard coding.
465 # Refer to: https://codereview.chromium.org/238253015
467 for zipalign_str in AddExeExtensions('zipalign'):
469 zipalign_path = Find(zipalign_str, sdk_root_path)
471 print('Use %s in %s.' % (zipalign_str, sdk_root_path))
475 if not zipalign_path:
476 print('zipalign could not be found in your Android SDK.'
477 ' Make sure it is installed.')
479 apk_path = '--unsigned-apk-path=' + os.path.join('out', 'app-unsigned.apk')
480 final_apk_path = '--final-apk-path=' + \
481 os.path.join('out', name + '.apk')
482 cmd = ['python', 'scripts/gyp/finalize_apk.py',
483 '--zipalign-path=%s' % zipalign_path,
486 '--keystore-path=%s' % key_store,
487 '--keystore-alias=%s' % key_alias,
488 '--keystore-passcode=%s' % key_code]
491 src_file = os.path.join('out', name + '.apk')
492 package_name = options.name
493 if options.app_version:
494 package_name += ('_' + options.app_version)
495 if options.mode == 'shared':
496 dst_file = os.path.join(options.target_dir, '%s.apk' % package_name)
497 elif options.mode == 'embedded':
498 dst_file = os.path.join(options.target_dir,
499 '%s_%s.apk' % (package_name, options.arch))
500 shutil.copyfile(src_file, dst_file)
502 if options.mode == 'embedded':
503 os.remove(pak_des_path)
506 def PrintPackageInfo(options, packaged_archs):
507 package_name_version = os.path.join(options.target_dir, options.name)
508 if options.app_version:
509 package_name_version += '_' + options.app_version
511 if len(packaged_archs) == 0:
512 print ('A non-platform specific APK for the web application "%s" was '
513 'generated successfully at\n%s.apk. It requires a shared Crosswalk '
514 'Runtime to be present.'
515 % (options.name, package_name_version))
518 for arch in packaged_archs:
519 print ('An APK for the web application "%s" including the Crosswalk '
520 'Runtime built for %s was generated successfully, which can be '
521 'found at\n%s_%s.apk.'
522 % (options.name, arch, package_name_version, arch))
524 all_archs = set(AllArchitectures())
526 if len(packaged_archs) != len(all_archs):
527 missed_archs = all_archs - set(packaged_archs)
528 print ('\n\nWARNING: ')
529 print ('This APK will only work on %s based Android devices. Consider '
530 'building for %s as well.' %
531 (', '.join(packaged_archs), ', '.join(missed_archs)))
533 print ('\n\n%d APKs were created for %s devices. '
534 % (len(all_archs), ', '.join(all_archs)))
535 print ('Please install the one that matches the processor architecture '
536 'of your device.\n\n')
537 print ('If you are going to submit this application to an application '
538 'store, please make sure you submit both packages.\nInstructions '
539 'for submitting multiple APKs to Google Play Store are available '
540 'here:\nhttps://software.intel.com/en-us/html5/articles/submitting'
541 '-multiple-crosswalk-apk-to-google-play-store')
543 def MakeApk(options, app_info):
544 Customize(options, app_info)
547 if options.mode == 'shared':
548 Execution(options, name)
549 elif options.mode == 'embedded':
551 Execution(options, name)
552 packaged_archs.append(options.arch)
554 # If the arch option is unspecified, all of available platform APKs
556 valid_archs = ['x86', 'armeabi-v7a']
557 for arch in valid_archs:
558 lib_path = os.path.join('native_libs', arch, 'libs',
559 arch, 'libxwalkcore.so')
560 if os.path.isfile(lib_path):
561 if arch.find('x86') != -1:
563 elif arch.find('arm') != -1:
565 Execution(options, name)
566 packaged_archs.append(options.arch)
568 print('Warning: failed to create package for arch "%s" '
569 'due to missing library %s' %
572 if len(packaged_archs) == 0:
573 print('No packages created, aborting')
576 PrintPackageInfo(options, packaged_archs)
579 parser = optparse.OptionParser()
580 parser.add_option('-v', '--version', action='store_true',
581 dest='version', default=False,
582 help='The version of this python tool.')
583 parser.add_option('--verbose', action="store_true",
584 dest='verbose', default=False,
585 help='Print debug messages.')
586 info = ('The packaging mode of the web application. The value \'shared\' '
587 'means that the runtime is shared across multiple application '
588 'instances and that the runtime needs to be distributed separately. '
589 'The value \'embedded\' means that the runtime is embedded into the '
590 'application itself and distributed along with it.'
591 'Set the default mode as \'embedded\'. For example: --mode=embedded')
592 parser.add_option('--mode', choices=('embedded', 'shared'),
593 default='embedded', help=info)
594 info = ('The target architecture of the embedded runtime. Supported values '
595 'are \'x86\' and \'arm\'. Note, if undefined, APKs for all possible '
596 'architestures will be generated.')
597 parser.add_option('--arch', choices=AllArchitectures(), help=info)
598 group = optparse.OptionGroup(parser, 'Application Source Options',
599 'This packaging tool supports 3 kinds of web application source: '
600 '1) XPK package; 2) manifest.json; 3) various command line options, '
601 'for example, \'--app-url\' for website, \'--app-root\' and '
602 '\'--app-local-path\' for local web application.')
603 info = ('The path of the XPK package. For example, --xpk=/path/to/xpk/file')
604 group.add_option('--xpk', help=info)
605 info = ('The manifest file with the detail description of the application. '
606 'For example, --manifest=/path/to/your/manifest/file')
607 group.add_option('--manifest', help=info)
608 info = ('The url of application. '
609 'This flag allows to package website as apk. For example, '
610 '--app-url=http://www.intel.com')
611 group.add_option('--app-url', help=info)
612 info = ('The root path of the web app. '
613 'This flag allows to package local web app as apk. For example, '
614 '--app-root=/root/path/of/the/web/app')
615 group.add_option('--app-root', help=info)
616 info = ('The relative path of entry file based on the value from '
617 '\'app_root\'. This flag should work with \'--app-root\' together. '
618 'For example, --app-local-path=/relative/path/of/entry/file')
619 group.add_option('--app-local-path', help=info)
620 parser.add_option_group(group)
621 group = optparse.OptionGroup(parser, 'Mandatory arguments',
622 'They are used for describing the APK information through '
623 'command line options.')
624 info = ('The apk name. For example, --name="Your Application Name"')
625 group.add_option('--name', help=info)
626 info = ('The package name. For example, '
627 '--package=com.example.YourPackage')
628 group.add_option('--package', help=info)
629 parser.add_option_group(group)
630 group = optparse.OptionGroup(parser, 'Optional arguments',
631 'They are used for various settings for applications through '
632 'command line options.')
633 info = ('The version name of the application. '
634 'For example, --app-version=1.0.0')
635 group.add_option('--app-version', help=info)
636 info = ('The version code of the application. '
637 'For example, --app-versionCode=24')
638 group.add_option('--app-versionCode', type='int', help=info)
639 info = ('The version code base of the application. Version code will '
640 'be made by adding a prefix based on architecture to the version '
641 'code base. For example, --app-versionCodeBase=24')
642 group.add_option('--app-versionCodeBase', type='int', help=info)
643 info = ('Use command lines.'
644 'Crosswalk is powered by Chromium and supports Chromium command line.'
646 '--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
647 group.add_option('--xwalk-command-line', default='', help=info)
648 info = ('The description of the application. For example, '
649 '--description=YourApplicationDescription')
650 group.add_option('--description', help=info)
651 group.add_option('--enable-remote-debugging', action='store_true',
652 dest='enable_remote_debugging', default=False,
653 help='Enable remote debugging.')
654 info = ('The list of external extension paths splitted by OS separators. '
655 'The separators are \':\' , \';\' and \':\' on Linux, Windows and '
656 'Mac OS respectively. For example, '
657 '--extensions=/path/to/extension1:/path/to/extension2.')
658 group.add_option('--extensions', help=info)
659 group.add_option('-f', '--fullscreen', action='store_true',
660 dest='fullscreen', default=False,
661 help='Make application fullscreen.')
662 group.add_option('--keep-screen-on', action='store_true', default=False,
663 help='Support keeping screen on')
664 info = ('The path of application icon. '
665 'Such as: --icon=/path/to/your/customized/icon')
666 group.add_option('--icon', help=info)
667 info = ('The orientation of the web app\'s display on the device. '
668 'For example, --orientation=landscape. The default value is '
669 '\'unspecified\'. The permitted values are from Android: '
670 'http://developer.android.com/guide/topics/manifest/'
671 'activity-element.html#screen')
672 group.add_option('--orientation', help=info)
673 info = ('The list of permissions to be used by web application. For example, '
674 '--permissions=geolocation:webgl')
675 group.add_option('--permissions', help=info)
676 info = ('Packaging tool will move the output APKS to the target directory')
677 group.add_option('--target-dir', default=os.getcwd(), help=info)
678 parser.add_option_group(group)
679 group = optparse.OptionGroup(parser, 'Keystore Options',
680 'The keystore is a signature from web developer, it\'s used when '
681 'developer wants to distribute the applications.')
682 info = ('The path to the developer keystore. For example, '
683 '--keystore-path=/path/to/your/developer/keystore')
684 group.add_option('--keystore-path', help=info)
685 info = ('The alias name of keystore. For example, --keystore-alias=name')
686 group.add_option('--keystore-alias', help=info)
687 info = ('The passcode of keystore. For example, --keystore-passcode=code')
688 group.add_option('--keystore-passcode', help=info)
689 info = ('Minify and obfuscate javascript and css.'
690 '--compressor: compress javascript and css.'
691 '--compressor=js: compress javascript.'
692 '--compressor=css: compress css.')
693 group.add_option('--compressor', dest='compressor', action='callback',
694 callback=ParseParameterForCompressor, type='string',
696 parser.add_option_group(group)
697 options, _ = parser.parse_args()
703 if os.path.isfile('VERSION'):
704 print(GetVersion('VERSION'))
707 parser.error('VERSION was not found, so Crosswalk\'s version could not '
712 xpk_name = os.path.splitext(os.path.basename(options.xpk))[0]
713 xpk_temp_dir = xpk_name + '_xpk'
714 ParseXPK(options, xpk_temp_dir)
716 if options.app_root and not options.manifest:
717 manifest_path = os.path.join(options.app_root, 'manifest.json')
718 if os.path.exists(manifest_path):
719 print('Using manifest.json distributed with the application.')
720 options.manifest = manifest_path
723 if not options.manifest:
725 VerifyAppName(options.package, 'packagename')
727 parser.error('A package name is required. Please use the "--package" '
730 VerifyAppName(options.name)
731 app_info.original_name = options.name
732 options.name = ReplaceSpaceWithUnderscore(options.name)
734 parser.error('An APK name is required. Please use the "--name" option.')
736 # The checks here are really convoluted, but at the moment make_apk
737 # misbehaves any of the following conditions is true.
739 # 1) --app-url must be passed without either --app-local-path or
741 if options.app_root or options.app_local_path:
742 parser.error('You must pass either "--app-url" or "--app-local-path" '
743 'with "--app-root", but not all.')
745 # 2) --app-url is not passed but only one of --app-local-path and
747 if bool(options.app_root) != bool(options.app_local_path):
748 parser.error('You must specify both "--app-local-path" and '
750 # 3) None of --app-url, --app-local-path and --app-root are passed.
751 elif not options.app_root and not options.app_local_path:
752 parser.error('You must pass either "--app-url" or "--app-local-path" '
753 'with "--app-root".')
755 if options.permissions:
756 permission_list = options.permissions.split(':')
758 print('Warning: all supported permissions on Android port are added. '
759 'Refer to https://github.com/crosswalk-project/'
760 'crosswalk-website/wiki/Crosswalk-manifest')
761 permission_list = permission_mapping_table.keys()
762 options.permissions = HandlePermissionList(permission_list)
763 options.icon_dict = {}
766 ParseManifest(options, app_info)
767 except SystemExit as ec:
770 if (options.app_root and options.app_local_path and
771 not os.path.isfile(os.path.join(options.app_root,
772 options.app_local_path))):
773 print('Please make sure that the local path file of launching app '
777 if options.target_dir:
778 target_dir = os.path.abspath(os.path.expanduser(options.target_dir))
779 options.target_dir = target_dir
780 if not os.path.isdir(target_dir):
781 os.makedirs(target_dir)
784 MakeApk(options, app_info)
785 except SystemExit as ec:
786 CleanDir(options.name)
788 CleanDir(xpk_temp_dir)
793 if __name__ == '__main__':
794 sys.exit(main(sys.argv))