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
8 import compress_js_and_css
17 sys.path.append('scripts/gyp')
18 from customize import ReplaceInvalidChars, CustomizeAll
19 from dex import AddExeExtensions
20 from handle_permissions import permission_mapping_table
21 from manifest_json_parser import HandlePermissionList
22 from manifest_json_parser import ManifestJsonParser
25 if os.path.exists(path):
29 def RunCommand(command, verbose=False, shell=False):
30 """Runs the command list, print the output, and propagate its result."""
31 proc = subprocess.Popen(command, stdout=subprocess.PIPE,
32 stderr=subprocess.STDOUT, shell=shell)
34 output = proc.communicate()[0]
35 result = proc.returncode
37 print(output.decode("utf-8").strip())
39 print ('Command "%s" exited with non-zero exit code %d'
40 % (' '.join(command), result))
45 """Search PATH for executable files with the given name."""
47 exts = [_f for _f in os.environ.get('PATHEXT', '').split(os.pathsep) if _f]
48 path = os.environ.get('PATH', None)
51 for p in os.environ.get('PATH', '').split(os.pathsep):
52 p = os.path.join(p, name)
53 if os.access(p, os.X_OK):
57 if os.access(pext, os.X_OK):
63 """Find executable file with the given name
64 and maximum API level under specific path."""
66 for root, _, files in os.walk(path):
68 key = os.path.join(root, name)
69 sdk_version = os.path.basename(os.path.dirname(key))
70 str_num = re.search(r'\d+', sdk_version)
72 result[key] = int(str_num.group())
77 return max(iter(result.items()), key=operator.itemgetter(1))[0]
81 """Get the version of this python tool."""
82 version_str = 'Crosswalk app packaging tool version is '
83 file_handle = open(path, 'r')
84 src_content = file_handle.read()
85 version_nums = re.findall(r'\d+', src_content)
86 version_str += ('.').join(version_nums)
91 def ParseManifest(options):
92 parser = ManifestJsonParser(os.path.expanduser(options.manifest))
93 if not options.package:
94 options.package = 'org.xwalk.' + parser.GetAppName().lower()
96 options.name = parser.GetAppName()
97 if not options.app_version:
98 options.app_version = parser.GetVersion()
99 if not options.app_versionCode and not options.app_versionCodeBase:
100 options.app_versionCode = 1
101 if parser.GetDescription():
102 options.description = parser.GetDescription()
103 if parser.GetPermissions():
104 options.permissions = parser.GetPermissions()
105 if parser.GetAppUrl():
106 options.app_url = parser.GetAppUrl()
107 elif parser.GetAppLocalPath():
108 options.app_local_path = parser.GetAppLocalPath()
110 print('Error: there is no app launch path defined in manifest.json.')
112 if parser.GetAppRoot():
113 options.app_root = parser.GetAppRoot()
114 options.icon_dict = parser.GetIcons()
115 if parser.GetFullScreenFlag().lower() == 'true':
116 options.fullscreen = True
117 elif parser.GetFullScreenFlag().lower() == 'false':
118 options.fullscreen = False
121 def ParseXPK(options, out_dir):
122 cmd = ['python', 'parse_xpk.py',
123 '--file=%s' % os.path.expanduser(options.xpk),
124 '--out=%s' % out_dir]
127 print ('Use the manifest from XPK by default '
128 'when "--xpk" option is specified, and '
129 'the "--manifest" option would be ignored.')
132 if os.path.isfile(os.path.join(out_dir, 'manifest.json')):
133 options.manifest = os.path.join(out_dir, 'manifest.json')
135 print('XPK doesn\'t contain manifest file.')
139 def FindExtensionJars(root_path):
140 ''' Find all .jar files for external extensions. '''
142 if not os.path.exists(root_path):
143 return extension_jars
145 for afile in os.listdir(root_path):
146 if os.path.isdir(os.path.join(root_path, afile)):
147 base_name = os.path.basename(afile)
148 extension_jar = os.path.join(root_path, afile, base_name + '.jar')
149 if os.path.isfile(extension_jar):
150 extension_jars.append(extension_jar)
151 return extension_jars
154 # Follows the recommendation from
155 # http://software.intel.com/en-us/blogs/2012/11/12/how-to-publish-
156 # your-apps-on-google-play-for-x86-based-android-devices-using
157 def MakeVersionCode(options):
158 ''' Construct a version code'''
159 if options.app_versionCode:
160 return options.app_versionCode
162 # First digit is ABI, ARM=2, x86=6
164 if options.arch == 'arm':
166 if options.arch == 'x86':
169 if options.app_versionCodeBase:
170 b = str(options.app_versionCodeBase)
172 print('Version code base must be 7 digits or less: '
173 'versionCodeBase=%s' % (b))
175 # zero pad to 7 digits, middle digits can be used for other
176 # features, according to recommendation in URL
177 return '%s%s' % (abi, b.zfill(7))
180 def Customize(options):
181 package = 'org.xwalk.app.template'
183 package = options.package
188 if options.app_version:
189 app_version = options.app_version
190 app_versionCode = MakeVersionCode(options)
193 app_root = os.path.expanduser(options.app_root)
194 remote_debugging = ''
195 if options.enable_remote_debugging:
196 remote_debugging = '--enable-remote-debugging'
198 if options.fullscreen:
199 fullscreen_flag = '-f'
200 orientation = 'unspecified'
201 if options.orientation:
202 orientation = options.orientation
205 icon = '%s' % os.path.expanduser(options.icon)
206 CustomizeAll(app_versionCode, options.description, options.icon_dict,
207 options.permissions, options.app_url, app_root,
208 options.app_local_path, remote_debugging,
209 fullscreen_flag, options.keep_screen_on, options.extensions,
210 options.manifest, icon, package, name, app_version,
211 orientation, options.xwalk_command_line)
214 def Execution(options, sanitized_name):
215 android_path_array = Which('android')
216 if not android_path_array:
217 print('Please install Android SDK first.')
220 sdk_root_path = os.path.dirname(os.path.dirname(android_path_array[0]))
223 sdk_jar_path = Find('android.jar', os.path.join(sdk_root_path, 'platforms'))
225 print('Your Android SDK may be ruined, please reinstall it.')
228 level_string = os.path.basename(os.path.dirname(sdk_jar_path))
229 api_level = int(re.search(r'\d+', level_string).group())
231 print('Please install Android API level (>=14) first.')
234 if options.keystore_path:
235 key_store = os.path.expanduser(options.keystore_path)
236 if options.keystore_alias:
237 key_alias = options.keystore_alias
239 print('Please provide an alias name of the developer key.')
241 if options.keystore_passcode:
242 key_code = options.keystore_passcode
244 print('Please provide the passcode of the developer key.')
247 print ('Use xwalk\'s keystore by default for debugging. '
248 'Please switch to your keystore when distributing it to app market.')
249 key_store = 'scripts/ant/xwalk-debug.keystore'
250 key_alias = 'xwalkdebugkey'
251 key_code = 'xwalkdebug'
253 if not os.path.exists('out'):
256 # Make sure to use ant-tasks.jar correctly.
257 # Default Android SDK names it as ant-tasks.jar
258 # Chrome third party Android SDk names it as anttasks.jar
259 ant_tasks_jar_path = os.path.join(sdk_root_path,
260 'tools', 'lib', 'ant-tasks.jar')
261 if not os.path.exists(ant_tasks_jar_path):
262 ant_tasks_jar_path = os.path.join(sdk_root_path,
263 'tools', 'lib' ,'anttasks.jar')
266 for aapt_str in AddExeExtensions('aapt'):
268 aapt_path = Find(aapt_str, sdk_root_path)
269 print('Use %s in %s.' % (aapt_str, sdk_root_path))
272 print('There doesn\'t exist %s in %s.' % (aapt_str, sdk_root_path))
274 print('Your Android SDK may be ruined, please reinstall it.')
277 # Check whether ant is installed.
279 cmd = ['ant', '-version']
280 RunCommand(cmd, shell=True)
281 except EnvironmentError:
282 print('Please install ant first.')
285 res_dirs = '-DADDITIONAL_RES_DIRS=\'\''
286 res_packages = '-DADDITIONAL_RES_PACKAGES=\'\''
287 res_r_text_files = '-DADDITIONAL_R_TEXT_FILES=\'\''
288 if options.mode == 'embedded':
289 # Prepare the .pak file for embedded mode.
290 pak_src_path = os.path.join('native_libs_res', 'xwalk.pak')
291 pak_des_path = os.path.join(sanitized_name, 'assets', 'xwalk.pak')
292 shutil.copy(pak_src_path, pak_des_path)
294 # Prepare the icudtl.dat for embedded mode.
295 icudtl_src_path = os.path.join('native_libs_res', 'icudtl.dat')
296 icudtl_des_path = os.path.join(sanitized_name, 'assets', 'icudtl.dat')
297 shutil.copy(icudtl_src_path, icudtl_des_path)
299 js_src_dir = os.path.join('native_libs_res', 'jsapi')
300 js_des_dir = os.path.join(sanitized_name, 'assets', 'jsapi')
301 if os.path.exists(js_des_dir):
302 shutil.rmtree(js_des_dir)
303 shutil.copytree(js_src_dir, js_des_dir)
305 res_ui_java = os.path.join('gen', 'ui_java')
306 res_content_java = os.path.join('gen', 'content_java')
307 res_xwalk_java = os.path.join('gen', 'xwalk_core_java')
308 res_dirs = ('-DADDITIONAL_RES_DIRS='
309 + os.path.join(res_ui_java, 'res_crunched') + ' '
310 + os.path.join(res_ui_java, 'res_v14_compatibility') + ' '
311 + os.path.join(res_ui_java, 'res_grit') + ' '
312 + os.path.join('libs_res', 'ui') + ' '
313 + os.path.join(res_content_java, 'res_crunched') + ' '
314 + os.path.join(res_content_java, 'res_v14_compatibility') + ' '
315 + os.path.join('libs_res', 'content') + ' '
316 + os.path.join(res_content_java, 'res_grit') + ' '
317 + os.path.join(res_xwalk_java, 'res_crunched') + ' '
318 + os.path.join(res_xwalk_java, 'res_v14_compatibility') + ' '
319 + os.path.join('libs_res', 'runtime') + ' '
320 + os.path.join(res_xwalk_java, 'res_grit'))
321 res_packages = ('-DADDITIONAL_RES_PACKAGES=org.chromium.ui '
322 'org.xwalk.core org.chromium.content')
323 res_r_text_files = ('-DADDITIONAL_R_TEXT_FILES='
324 + os.path.join(res_ui_java, 'java_R', 'R.txt') + ' '
325 + os.path.join(res_xwalk_java, 'java_R', 'R.txt') + ' '
326 + os.path.join(res_content_java, 'java_R', 'R.txt'))
328 resource_dir = '-DRESOURCE_DIR=' + os.path.join(sanitized_name, 'res')
329 manifest_path = os.path.join(sanitized_name, 'AndroidManifest.xml')
330 cmd = ['python', os.path.join('scripts', 'gyp', 'ant.py'),
331 '-DAAPT_PATH=%s' % aapt_path,
335 '-DANDROID_MANIFEST=%s' % manifest_path,
336 '-DANDROID_SDK_JAR=%s' % sdk_jar_path,
337 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
338 '-DANDROID_SDK_VERSION=%d' % api_level,
339 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
340 '-DLIBRARY_MANIFEST_PATHS= ',
343 '-DSTAMP=codegen.stamp',
346 os.path.join('scripts', 'ant', 'apk-codegen.xml')]
347 RunCommand(cmd, options.verbose)
349 # Check whether java is installed.
351 cmd = ['java', '-version']
352 RunCommand(cmd, shell=True)
353 except EnvironmentError:
354 print('Please install Oracle JDK first.')
357 # Compile App source code with app runtime code.
358 classpath = '--classpath='
359 classpath += os.path.join(os.getcwd(), 'libs',
360 'xwalk_app_runtime_java.jar')
361 classpath += ' ' + sdk_jar_path
362 src_dirs = '--src-dirs=' + os.path.join(os.getcwd(), sanitized_name, 'src') +\
363 ' ' + os.path.join(os.getcwd(), 'out', 'gen')
364 cmd = ['python', os.path.join('scripts', 'gyp', 'javac.py'),
365 '--output-dir=%s' % os.path.join('out', 'classes'),
370 '--stamp=compile.stam']
374 asset_dir = '-DASSET_DIR=%s' % os.path.join(sanitized_name, 'assets')
375 xml_path = os.path.join('scripts', 'ant', 'apk-package-resources.xml')
376 cmd = ['python', os.path.join('scripts', 'gyp', 'ant.py'),
377 '-DAAPT_PATH=%s' % aapt_path,
381 '-DANDROID_SDK_JAR=%s' % sdk_jar_path,
382 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
383 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
384 '-DAPK_NAME=%s' % sanitized_name,
385 '-DAPP_MANIFEST_VERSION_CODE=0',
386 '-DAPP_MANIFEST_VERSION_NAME=Developer Build',
388 '-DCONFIGURATION_NAME=Release',
391 '-DSTAMP=package_resources.stamp',
395 RunCommand(cmd, options.verbose)
397 dex_path = '--dex-path=' + os.path.join(os.getcwd(), 'out', 'classes.dex')
398 app_runtime_jar = os.path.join(os.getcwd(),
399 'libs', 'xwalk_app_runtime_java.jar')
401 # Check whether external extensions are included.
402 extensions_string = 'xwalk-extensions'
403 extensions_dir = os.path.join(os.getcwd(), sanitized_name, extensions_string)
404 external_extension_jars = FindExtensionJars(extensions_dir)
406 if options.mode == 'embedded':
407 input_jars.append(os.path.join(os.getcwd(), 'libs',
408 'xwalk_runtime_embedded.dex.jar'))
409 dex_command_list = ['python', os.path.join('scripts', 'gyp', 'dex.py'),
411 '--android-sdk-root=%s' % sdk_root_path,
413 os.path.join(os.getcwd(), 'out', 'classes')]
414 dex_command_list.extend(external_extension_jars)
415 dex_command_list.extend(input_jars)
416 RunCommand(dex_command_list)
418 src_dir = '-DSOURCE_DIR=' + os.path.join(sanitized_name, 'src')
419 apk_path = '-DUNSIGNED_APK_PATH=' + os.path.join('out', 'app-unsigned.apk')
420 native_lib_path = '-DNATIVE_LIBS_DIR='
421 if options.mode == 'embedded':
422 if options.arch == 'x86':
423 x86_native_lib_path = os.path.join('native_libs', 'x86', 'libs',
424 'x86', 'libxwalkcore.so')
425 if os.path.isfile(x86_native_lib_path):
426 native_lib_path += os.path.join('native_libs', 'x86', 'libs')
428 print('Missing x86 native library for Crosswalk embedded APK. Abort!')
430 elif options.arch == 'arm':
431 arm_native_lib_path = os.path.join('native_libs', 'armeabi-v7a', 'libs',
432 'armeabi-v7a', 'libxwalkcore.so')
433 if os.path.isfile(arm_native_lib_path):
434 native_lib_path += os.path.join('native_libs', 'armeabi-v7a', 'libs')
436 print('Missing ARM native library for Crosswalk embedded APK. Abort!')
438 # A space is needed for Windows.
439 native_lib_path += ' '
440 cmd = ['python', 'scripts/gyp/ant.py',
441 '-DANDROID_SDK_ROOT=%s' % sdk_root_path,
442 '-DANT_TASKS_JAR=%s' % ant_tasks_jar_path,
443 '-DAPK_NAME=%s' % sanitized_name,
444 '-DCONFIGURATION_NAME=Release',
451 'scripts/ant/apk-package.xml']
452 RunCommand(cmd, options.verbose)
454 apk_path = '--unsigned-apk-path=' + os.path.join('out', 'app-unsigned.apk')
455 final_apk_path = '--final-apk-path=' + \
456 os.path.join('out', sanitized_name + '.apk')
457 cmd = ['python', 'scripts/gyp/finalize_apk.py',
458 '--android-sdk-root=%s' % sdk_root_path,
461 '--keystore-path=%s' % key_store,
462 '--keystore-alias=%s' % key_alias,
463 '--keystore-passcode=%s' % key_code]
466 src_file = os.path.join('out', sanitized_name + '.apk')
467 package_name = options.name
468 if options.app_version:
469 package_name += ('_' + options.app_version)
470 if options.mode == 'shared':
471 dst_file = os.path.join(options.target_dir, '%s.apk' % package_name)
472 elif options.mode == 'embedded':
473 dst_file = os.path.join(options.target_dir,
474 '%s_%s.apk' % (package_name, options.arch))
475 shutil.copyfile(src_file, dst_file)
477 if options.mode == 'embedded':
478 os.remove(pak_des_path)
481 def PrintPackageInfo(target_dir, app_name, app_version,
482 arch = '', multi_arch = False):
483 package_name_version = os.path.join(target_dir, app_name)
484 if app_version != '':
485 package_name_version += ('_' + app_version)
487 print ('A non-platform specific APK for the web application "%s" was '
488 'generated successfully at\n%s.apk. It requires a shared Crosswalk '
489 'Runtime to be present.'
490 % (app_name, package_name_version))
492 print ('An APK for the web application "%s" including the Crosswalk '
493 'Runtime built for %s was generated successfully, which can be '
494 'found at\n%s_%s.apk.'
495 % (app_name, arch, package_name_version, arch))
496 if multi_arch == False:
498 print ('WARNING: This APK will only work on x86 based Android devices. '
499 'Consider building for ARM as well.')
501 print ('WARNING: This APK will only work on ARM based Android devices. '
502 'Consider building for x86 as well.')
505 def MakeApk(options, sanitized_name):
508 if options.app_version:
509 app_version = options.app_version
510 if options.mode == 'shared':
511 Execution(options, sanitized_name)
512 PrintPackageInfo(options.target_dir, sanitized_name, app_version)
513 elif options.mode == 'embedded':
515 Execution(options, sanitized_name)
516 PrintPackageInfo(options.target_dir, sanitized_name,
517 app_version, options.arch)
519 # If the arch option is unspecified, all of available platform APKs
521 valid_archs = ['x86', 'armeabi-v7a']
523 for arch in valid_archs:
524 lib_path = os.path.join('native_libs', arch, 'libs',
525 arch, 'libxwalkcore.so')
526 if os.path.isfile(lib_path):
527 if arch.find('x86') != -1:
529 elif arch.find('arm') != -1:
531 Execution(options, sanitized_name)
532 packaged_archs.append(options.arch)
534 print('Warning: failed to create package for arch "%s" '
535 'due to missing library %s' %
538 if len(packaged_archs) == 0:
539 print('No packages created, aborting')
543 if len(packaged_archs) >=2:
545 for arch in packaged_archs:
546 PrintPackageInfo(options.target_dir, sanitized_name,
547 app_version, arch, multi_arch)
549 print('Unknown mode for packaging the application. Abort!')
553 def parse_optional_arg(default_value):
554 def func(option, value, values, parser):
557 if parser.rargs and not parser.rargs[0].startswith('-'):
558 val = parser.rargs[0]
562 setattr(parser.values, option.dest, val)
567 parser = optparse.OptionParser()
568 parser.add_option('-v', '--version', action='store_true',
569 dest='version', default=False,
570 help='The version of this python tool.')
571 parser.add_option('--verbose', action="store_true",
572 dest='verbose', default=False,
573 help='Print debug messages.')
574 info = ('The packaging mode of the web application. The value \'shared\' '
575 'means that the runtime is shared across multiple application '
576 'instances and that the runtime needs to be distributed separately. '
577 'The value \'embedded\' means that the runtime is embedded into the '
578 'application itself and distributed along with it.'
579 'Set the default mode as \'embedded\'. For example: --mode=embedded')
580 parser.add_option('--mode', default='embedded', help=info)
581 info = ('The target architecture of the embedded runtime. Supported values '
582 'are \'x86\' and \'arm\'. Note, if undefined, APKs for all possible '
583 'architestures will be generated.')
584 parser.add_option('--arch', help=info)
585 group = optparse.OptionGroup(parser, 'Application Source Options',
586 'This packaging tool supports 3 kinds of web application source: '
587 '1) XPK package; 2) manifest.json; 3) various command line options, '
588 'for example, \'--app-url\' for website, \'--app-root\' and '
589 '\'--app-local-path\' for local web application.')
590 info = ('The path of the XPK package. For example, --xpk=/path/to/xpk/file')
591 group.add_option('--xpk', help=info)
592 info = ('The manifest file with the detail description of the application. '
593 'For example, --manifest=/path/to/your/manifest/file')
594 group.add_option('--manifest', help=info)
595 info = ('The url of application. '
596 'This flag allows to package website as apk. For example, '
597 '--app-url=http://www.intel.com')
598 group.add_option('--app-url', help=info)
599 info = ('The root path of the web app. '
600 'This flag allows to package local web app as apk. For example, '
601 '--app-root=/root/path/of/the/web/app')
602 group.add_option('--app-root', help=info)
603 info = ('The relative path of entry file based on the value from '
604 '\'app_root\'. This flag should work with \'--app-root\' together. '
605 'For example, --app-local-path=/relative/path/of/entry/file')
606 group.add_option('--app-local-path', help=info)
607 parser.add_option_group(group)
608 group = optparse.OptionGroup(parser, 'Mandatory arguments',
609 'They are used for describing the APK information through '
610 'command line options.')
611 info = ('The apk name. For example, --name="Your Application Name"')
612 group.add_option('--name', help=info)
613 info = ('The package name. For example, '
614 '--package=com.example.YourPackage')
615 group.add_option('--package', help=info)
616 parser.add_option_group(group)
617 group = optparse.OptionGroup(parser, 'Optional arguments',
618 'They are used for various settings for applications through '
619 'command line options.')
620 info = ('The version name of the application. '
621 'For example, --app-version=1.0.0')
622 group.add_option('--app-version', help=info)
623 info = ('The version code of the application. '
624 'For example, --app-versionCode=24')
625 group.add_option('--app-versionCode', type='int', help=info)
626 info = ('The version code base of the application. Version code will '
627 'be made by adding a prefix based on architecture to the version '
628 'code base. For example, --app-versionCodeBase=24')
629 group.add_option('--app-versionCodeBase', type='int', help=info)
630 info = ('Use command lines.'
631 'Crosswalk is powered by Chromium and supports Chromium command line.'
633 '--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
634 group.add_option('--xwalk-command-line', default='', help=info)
635 info = ('The description of the application. For example, '
636 '--description=YourApplicationDescription')
637 group.add_option('--description', help=info)
638 group.add_option('--enable-remote-debugging', action='store_true',
639 dest='enable_remote_debugging', default=False,
640 help = 'Enable remote debugging.')
641 info = ('The list of external extension paths splitted by OS separators. '
642 'The separators are \':\' , \';\' and \':\' on Linux, Windows and '
643 'Mac OS respectively. For example, '
644 '--extensions=/path/to/extension1:/path/to/extension2.')
645 group.add_option('--extensions', help=info)
646 group.add_option('-f', '--fullscreen', action='store_true',
647 dest='fullscreen', default=False,
648 help='Make application fullscreen.')
649 group.add_option('--keep-screen-on', action='store_true', default=False,
650 help='Support keeping screen on')
651 info = ('The path of application icon. '
652 'Such as: --icon=/path/to/your/customized/icon')
653 group.add_option('--icon', help=info)
654 info = ('The orientation of the web app\'s display on the device. '
655 'For example, --orientation=landscape. The default value is '
656 '\'unspecified\'. The permitted values are from Android: '
657 'http://developer.android.com/guide/topics/manifest/'
658 'activity-element.html#screen')
659 group.add_option('--orientation', help=info)
660 info = ('The list of permissions to be used by web application. For example, '
661 '--permissions=geolocation:webgl')
662 group.add_option('--permissions', help=info)
663 info = ('Packaging tool will move the output APKS to the target directory')
664 group.add_option('--target-dir', default=os.getcwd(), help=info)
665 parser.add_option_group(group)
666 group = optparse.OptionGroup(parser, 'Keystore Options',
667 'The keystore is a signature from web developer, it\'s used when '
668 'developer wants to distribute the applications.')
669 info = ('The path to the developer keystore. For example, '
670 '--keystore-path=/path/to/your/developer/keystore')
671 group.add_option('--keystore-path', help=info)
672 info = ('The alias name of keystore. For example, --keystore-alias=name')
673 group.add_option('--keystore-alias', help=info)
674 info = ('The passcode of keystore. For example, --keystore-passcode=code')
675 group.add_option('--keystore-passcode', help=info)
676 info = ('Minify and obfuscate javascript and css.'
677 '--compressor: compress javascript and css.'
678 '--compressor=js: compress javascript.'
679 '--compressor=css: compress css.')
680 group.add_option('--compressor', dest='compressor', action='callback',
681 callback=parse_optional_arg('all'), help=info)
682 parser.add_option_group(group)
683 options, _ = parser.parse_args()
689 if os.path.isfile('VERSION'):
690 print(GetVersion('VERSION'))
693 parser.error('Can\'t get version due to the VERSION file missing!')
697 xpk_name = os.path.splitext(os.path.basename(options.xpk))[0]
698 xpk_temp_dir = xpk_name + '_xpk'
699 ParseXPK(options, xpk_temp_dir)
701 if options.app_root and not options.manifest:
702 manifest_path = os.path.join(options.app_root, 'manifest.json')
703 if os.path.exists(manifest_path):
704 print('Using manifest.json distributed with the application.')
705 options.manifest = manifest_path
707 if not options.manifest:
708 if not options.package:
709 parser.error('The package name is required! '
710 'Please use "--package" option.')
711 elif len(options.package) >= 128 :
712 parser.error('To be safe, the length of package name '
713 'should be less than 128.')
716 parser.error('The APK name is required! Please use "--name" option.')
717 if not ((options.app_url and not options.app_root
718 and not options.app_local_path) or ((not options.app_url)
719 and options.app_root and options.app_local_path)):
720 parser.error('The entry is required. If the entry is a remote url, '
721 'please use "--app-url" option; If the entry is local, '
722 'please use "--app-root" and '
723 '"--app-local-path" options together!')
724 if options.permissions:
725 permission_list = options.permissions.split(':')
727 print('Warning: all supported permissions on Android port are added. '
728 'Refer to https://github.com/crosswalk-project/'
729 'crosswalk-website/wiki/Crosswalk-manifest')
730 permission_list = permission_mapping_table.keys()
731 options.permissions = HandlePermissionList(permission_list)
732 options.icon_dict = {}
735 ParseManifest(options)
736 except SystemExit as ec:
739 if (options.app_root and options.app_local_path and not
740 os.path.isfile(os.path.join(options.app_root, options.app_local_path))):
741 print('Please make sure that the local path file of launching app '
745 options.name = ReplaceInvalidChars(options.name, 'apkname')
746 options.package = ReplaceInvalidChars(options.package)
747 sanitized_name = ReplaceInvalidChars(options.name, 'apkname')
749 if options.target_dir:
750 target_dir = os.path.abspath(os.path.expanduser(options.target_dir))
751 options.target_dir = target_dir
752 if not os.path.isdir(target_dir):
753 os.makedirs(target_dir)
756 compress = compress_js_and_css.CompressJsAndCss(options.app_root)
757 if options.compressor == 'all':
758 compress.CompressJavaScript()
759 compress.CompressCss()
760 elif options.compressor == 'js':
761 compress.CompressJavaScript()
762 elif options.compressor == 'css':
763 compress.CompressCss()
764 MakeApk(options, sanitized_name)
765 except SystemExit as ec:
766 CleanDir(sanitized_name)
768 if os.path.exists(xpk_temp_dir):
769 CleanDir(xpk_temp_dir)
774 if __name__ == '__main__':
775 sys.exit(main(sys.argv))