def __init__(self): argv = sys.argv # Buildozer used to pass these arguments in a now-invalid order # If that happens, apply this fix # This fix will be removed once a fixed buildozer is released if (len(argv) > 2 and argv[1].startswith('--color') and argv[2].startswith('--storage-dir')): argv.append(argv.pop(1)) # the --color arg argv.append(argv.pop(1)) # the --storage-dir arg parser = NoAbbrevParser( description=('A packaging tool for turning Python scripts and apps ' 'into Android APKs')) generic_parser = argparse.ArgumentParser( add_help=False, description=('Generic arguments applied to all commands')) dist_parser = argparse.ArgumentParser( add_help=False, description=('Arguments for dist building')) generic_parser.add_argument( '--debug', dest='debug', action='store_true', default=False, help='Display debug output and all build info') generic_parser.add_argument( '--color', dest='color', choices=['always', 'never', 'auto'], help='Enable or disable color output (default enabled on tty)') generic_parser.add_argument( '--sdk-dir', '--sdk_dir', dest='sdk_dir', default='', help='The filepath where the Android SDK is installed') generic_parser.add_argument( '--ndk-dir', '--ndk_dir', dest='ndk_dir', default='', help='The filepath where the Android NDK is installed') generic_parser.add_argument( '--android-api', '--android_api', dest='android_api', default=0, type=int, help='The Android API level to build against.') generic_parser.add_argument( '--ndk-version', '--ndk_version', dest='ndk_version', default='', help=('The version of the Android NDK. This is optional, ' 'we try to work it out automatically from the ndk_dir.')) generic_parser.add_argument( '--symlink-java-src', '--symlink_java_src', action='store_true', dest='symlink_java_src', default=False, help=('If True, symlinks the java src folder during build and dist ' 'creation. This is useful for development only, it could also ' 'cause weird problems.')) default_storage_dir = user_data_dir('python-for-android') if ' ' in default_storage_dir: default_storage_dir = '~/.python-for-android' generic_parser.add_argument( '--storage-dir', dest='storage_dir', default=default_storage_dir, help=('Primary storage directory for downloads and builds ' '(default: {})'.format(default_storage_dir))) generic_parser.add_argument( '--arch', help='The archs to build for, separated by commas.', default='armeabi') # Options for specifying the Distribution generic_parser.add_argument( '--dist-name', '--dist_name', help='The name of the distribution to use or create', default='') generic_parser.add_argument( '--requirements', help=('Dependencies of your app, should be recipe names or ' 'Python modules'), default='') generic_parser.add_argument( '--bootstrap', help='The bootstrap to build with. Leave unset to choose automatically.', default=None) generic_parser.add_argument( '--hook', help='Filename to a module that contain python-for-android hooks', default=None) add_boolean_option( generic_parser, ["force-build"], default=False, description='Whether to force compilation of a new distribution:') add_boolean_option( generic_parser, ["require-perfect-match"], default=False, description=('Whether the dist recipes must perfectly match ' 'those requested')) generic_parser.add_argument( '--local-recipes', '--local_recipes', dest='local_recipes', default='./p4a-recipes', help='Directory to look for local recipes') generic_parser.add_argument( '--java-build-tool', dest='java_build_tool', default='auto', choices=['auto', 'ant', 'gradle', 'none'], help=('The java build tool to use when packaging the APK, defaults ' 'to automatically selecting an appropriate tool.')) add_boolean_option( generic_parser, ['copy-libs'], default=False, description='Copy libraries instead of using biglink (Android 4.3+)') self._read_configuration() subparsers = parser.add_subparsers(dest='subparser_name', help='The command to run') def add_parser(subparsers, *args, **kwargs): ''' argparse in python2 doesn't support the aliases option, so we just don't provide the aliases there. ''' if 'aliases' in kwargs and sys.version_info.major < 3: kwargs.pop('aliases') return subparsers.add_parser(*args, **kwargs) parser_recipes = add_parser(subparsers, 'recipes', parents=[generic_parser], help='List the available recipes') parser_recipes.add_argument( "--compact", action="store_true", default=False, help="Produce a compact list suitable for scripting") parser_bootstraps = add_parser( subparsers, 'bootstraps', help='List the available bootstraps', parents=[generic_parser]) parser_clean_all = add_parser( subparsers, 'clean_all', aliases=['clean-all'], help='Delete all builds, dists and caches', parents=[generic_parser]) parser_clean_dists = add_parser( subparsers, 'clean_dists', aliases=['clean-dists'], help='Delete all dists', parents=[generic_parser]) parser_clean_bootstrap_builds = add_parser( subparsers, 'clean_bootstrap_builds', aliases=['clean-bootstrap-builds'], help='Delete all bootstrap builds', parents=[generic_parser]) parser_clean_builds = add_parser( subparsers, 'clean_builds', aliases=['clean-builds'], help='Delete all builds', parents=[generic_parser]) parser_clean = add_parser(subparsers, 'clean', help='Delete build components.', parents=[generic_parser]) parser_clean.add_argument( 'component', nargs='+', help=('The build component(s) to delete. You can pass any ' 'number of arguments from "all", "builds", "dists", ' '"distributions", "bootstrap_builds", "downloads".')) parser_clean_recipe_build = add_parser(subparsers, 'clean_recipe_build', aliases=['clean-recipe-build'], help=('Delete the build components of the given recipe. ' 'By default this will also delete built dists'), parents=[generic_parser]) parser_clean_recipe_build.add_argument('recipe', help='The recipe name') parser_clean_recipe_build.add_argument('--no-clean-dists', default=False, dest='no_clean_dists', action='store_true', help='If passed, do not delete existing dists') parser_clean_download_cache= add_parser(subparsers, 'clean_download_cache', aliases=['clean-download-cache'], help='Delete cached downloads for requirement builds', parents=[generic_parser]) parser_clean_download_cache.add_argument( 'recipes', nargs='*', help=('The recipes to clean (space-separated). If no recipe name is ' 'provided, the entire cache is cleared.')) parser_export_dist = add_parser(subparsers, 'export_dist', aliases=['export-dist'], help='Copy the named dist to the given path', parents=[generic_parser]) parser_export_dist.add_argument('output_dir', help=('The output dir to copy to')) parser_export_dist.add_argument('--symlink', action='store_true', help=('Symlink the dist instead of copying')) parser_apk = add_parser(subparsers, 'apk', help='Build an APK', parents=[generic_parser]) parser_apk.add_argument('--release', dest='build_mode', action='store_const', const='release', default='debug', help='Build the PARSER_APK. in Release mode') parser_apk.add_argument('--keystore', dest='keystore', action='store', default=None, help=('Keystore for JAR signing key, will use jarsigner ' 'default if not specified (release build only)')) parser_apk.add_argument('--signkey', dest='signkey', action='store', default=None, help='Key alias to sign PARSER_APK. with (release build only)') parser_apk.add_argument('--keystorepw', dest='keystorepw', action='store', default=None, help='Password for keystore') parser_apk.add_argument('--signkeypw', dest='signkeypw', action='store', default=None, help='Password for key alias') parser_create = add_parser(subparsers, 'create', help='Compile a set of requirements into a dist', parents=[generic_parser]) parser_archs = add_parser(subparsers, 'archs', help='List the available target architectures', parents=[generic_parser]) parser_distributions = add_parser(subparsers, 'distributions', aliases=['dists'], help='List the currently available (compiled) dists', parents=[generic_parser]) parser_delete_dist = add_parser(subparsers, 'delete_dist', aliases=['delete-dist'], help='Delete a compiled dist', parents=[generic_parser]) parser_sdk_tools = add_parser(subparsers, 'sdk_tools', aliases=['sdk-tools'], help='Run the given binary from the SDK tools dis', parents=[generic_parser]) parser_sdk_tools.add_argument( 'tool', help=('The tool binary name to run')) parser_adb = add_parser(subparsers, 'adb', help='Run adb from the given SDK', parents=[generic_parser]) parser_logcat = add_parser(subparsers, 'logcat', help='Run logcat from the given SDK', parents=[generic_parser]) parser_build_status = add_parser(subparsers, 'build_status', aliases=['build-status'], help='Print some debug information about current built components', parents=[generic_parser]) parser.add_argument('-v', '--version', action='version', version=__version__) args, unknown = parser.parse_known_args(sys.argv[1:]) args.unknown_args = unknown self.args = args if args.subparser_name is None: parser.print_help() exit(1) setup_color(args.color) if args.debug: logger.setLevel(logging.DEBUG) # strip version from requirements, and put them in environ if hasattr(args, 'requirements'): requirements = [] for requirement in split_argument_list(args.requirements): if "==" in requirement: requirement, version = requirement.split(u"==", 1) os.environ["VERSION_{}".format(requirement)] = version info('Recipe {}: version "{}" requested'.format( requirement, version)) requirements.append(requirement) args.requirements = u",".join(requirements) self.ctx = Context() self.storage_dir = args.storage_dir self.ctx.setup_dirs(self.storage_dir) self.sdk_dir = args.sdk_dir self.ndk_dir = args.ndk_dir self.android_api = args.android_api self.ndk_version = args.ndk_version self.ctx.symlink_java_src = args.symlink_java_src self.ctx.java_build_tool = args.java_build_tool self._archs = split_argument_list(args.arch) self.ctx.local_recipes = args.local_recipes self.ctx.copy_libs = args.copy_libs # Each subparser corresponds to a method getattr(self, args.subparser_name.replace('-', '_'))(args)
def __init__(self): argv = sys.argv self.warn_on_carriage_return_args(argv) # Buildozer used to pass these arguments in a now-invalid order # If that happens, apply this fix # This fix will be removed once a fixed buildozer is released if (len(argv) > 2 and argv[1].startswith('--color') and argv[2].startswith('--storage-dir')): argv.append(argv.pop(1)) # the --color arg argv.append(argv.pop(1)) # the --storage-dir arg parser = NoAbbrevParser( description='A packaging tool for turning Python scripts and apps ' 'into Android APKs') generic_parser = argparse.ArgumentParser( add_help=False, description='Generic arguments applied to all commands') argparse.ArgumentParser( add_help=False, description='Arguments for dist building') generic_parser.add_argument( '--debug', dest='debug', action='store_true', default=False, help='Display debug output and all build info') generic_parser.add_argument( '--color', dest='color', choices=['always', 'never', 'auto'], help='Enable or disable color output (default enabled on tty)') generic_parser.add_argument( '--sdk-dir', '--sdk_dir', dest='sdk_dir', default='', help='The filepath where the Android SDK is installed') generic_parser.add_argument( '--ndk-dir', '--ndk_dir', dest='ndk_dir', default='', help='The filepath where the Android NDK is installed') generic_parser.add_argument( '--android-api', '--android_api', dest='android_api', default=0, type=int, help=('The Android API level to build against defaults to {} if ' 'not specified.').format(RECOMMENDED_TARGET_API)) generic_parser.add_argument( '--ndk-version', '--ndk_version', dest='ndk_version', default=None, help=('DEPRECATED: the NDK version is now found automatically or ' 'not at all.')) generic_parser.add_argument( '--ndk-api', type=int, default=None, help=('The Android API level to compile against. This should be your ' '*minimal supported* API, not normally the same as your --android-api. ' 'Defaults to min(ANDROID_API, {}) if not specified.').format(RECOMMENDED_NDK_API)) generic_parser.add_argument( '--symlink-java-src', '--symlink_java_src', action='store_true', dest='symlink_java_src', default=False, help=('If True, symlinks the java src folder during build and dist ' 'creation. This is useful for development only, it could also' ' cause weird problems.')) default_storage_dir = user_data_dir('python-for-android') if ' ' in default_storage_dir: default_storage_dir = '~/.python-for-android' generic_parser.add_argument( '--storage-dir', dest='storage_dir', default=default_storage_dir, help=('Primary storage directory for downloads and builds ' '(default: {})'.format(default_storage_dir))) generic_parser.add_argument( '--arch', help='The arch to build for.', default='armeabi-v7a') # Options for specifying the Distribution generic_parser.add_argument( '--dist-name', '--dist_name', help='The name of the distribution to use or create', default='') generic_parser.add_argument( '--requirements', help=('Dependencies of your app, should be recipe names or ' 'Python modules. NOT NECESSARY if you are using ' 'Python 3 with --use-setup-py'), default='') generic_parser.add_argument( '--recipe-blacklist', help=('Blacklist an internal recipe from use. Allows ' 'disabling Python 3 core modules to save size'), dest="recipe_blacklist", default='') generic_parser.add_argument( '--blacklist-requirements', help=('Blacklist an internal recipe from use. Allows ' 'disabling Python 3 core modules to save size'), dest="blacklist_requirements", default='') generic_parser.add_argument( '--bootstrap', help='The bootstrap to build with. Leave unset to choose ' 'automatically.', default=None) generic_parser.add_argument( '--hook', help='Filename to a module that contains python-for-android hooks', default=None) add_boolean_option( generic_parser, ["force-build"], default=False, description='Whether to force compilation of a new distribution') add_boolean_option( generic_parser, ["require-perfect-match"], default=False, description=('Whether the dist recipes must perfectly match ' 'those requested')) add_boolean_option( generic_parser, ["allow-replace-dist"], default=True, description='Whether existing dist names can be automatically replaced' ) generic_parser.add_argument( '--local-recipes', '--local_recipes', dest='local_recipes', default='./p4a-recipes', help='Directory to look for local recipes') generic_parser.add_argument( '--java-build-tool', dest='java_build_tool', default='auto', choices=['auto', 'ant', 'gradle'], help=('The java build tool to use when packaging the APK, defaults ' 'to automatically selecting an appropriate tool.')) add_boolean_option( generic_parser, ['copy-libs'], default=False, description='Copy libraries instead of using biglink (Android 4.3+)' ) self._read_configuration() subparsers = parser.add_subparsers(dest='subparser_name', help='The command to run') def add_parser(subparsers, *args, **kwargs): """ argparse in python2 doesn't support the aliases option, so we just don't provide the aliases there. """ if 'aliases' in kwargs and sys.version_info.major < 3: kwargs.pop('aliases') return subparsers.add_parser(*args, **kwargs) add_parser( subparsers, 'recommendations', parents=[generic_parser], help='List recommended p4a dependencies') parser_recipes = add_parser( subparsers, 'recipes', parents=[generic_parser], help='List the available recipes') parser_recipes.add_argument( "--compact", action="store_true", default=False, help="Produce a compact list suitable for scripting") add_parser( subparsers, 'bootstraps', help='List the available bootstraps', parents=[generic_parser]) add_parser( subparsers, 'clean_all', aliases=['clean-all'], help='Delete all builds, dists and caches', parents=[generic_parser]) add_parser( subparsers, 'clean_dists', aliases=['clean-dists'], help='Delete all dists', parents=[generic_parser]) add_parser( subparsers, 'clean_bootstrap_builds', aliases=['clean-bootstrap-builds'], help='Delete all bootstrap builds', parents=[generic_parser]) add_parser( subparsers, 'clean_builds', aliases=['clean-builds'], help='Delete all builds', parents=[generic_parser]) parser_clean = add_parser( subparsers, 'clean', help='Delete build components.', parents=[generic_parser]) parser_clean.add_argument( 'component', nargs='+', help=('The build component(s) to delete. You can pass any ' 'number of arguments from "all", "builds", "dists", ' '"distributions", "bootstrap_builds", "downloads".')) parser_clean_recipe_build = add_parser( subparsers, 'clean_recipe_build', aliases=['clean-recipe-build'], help=('Delete the build components of the given recipe. ' 'By default this will also delete built dists'), parents=[generic_parser]) parser_clean_recipe_build.add_argument( 'recipe', help='The recipe name') parser_clean_recipe_build.add_argument( '--no-clean-dists', default=False, dest='no_clean_dists', action='store_true', help='If passed, do not delete existing dists') parser_clean_download_cache = add_parser( subparsers, 'clean_download_cache', aliases=['clean-download-cache'], help='Delete cached downloads for requirement builds', parents=[generic_parser]) parser_clean_download_cache.add_argument( 'recipes', nargs='*', help='The recipes to clean (space-separated). If no recipe name is' ' provided, the entire cache is cleared.') parser_export_dist = add_parser( subparsers, 'export_dist', aliases=['export-dist'], help='Copy the named dist to the given path', parents=[generic_parser]) parser_export_dist.add_argument('output_dir', help='The output dir to copy to') parser_export_dist.add_argument( '--symlink', action='store_true', help='Symlink the dist instead of copying') parser_apk = add_parser( subparsers, 'apk', help='Build an APK', parents=[generic_parser]) # This is actually an internal argument of the build.py # (see pythonforandroid/bootstraps/common/build/build.py). # However, it is also needed before the distribution is finally # assembled for locating the setup.py / other build systems, which # is why we also add it here: parser_apk.add_argument( '--private', dest='private', help='the directory with the app source code files' + ' (containing your main.py entrypoint)', required=False, default=None) parser_apk.add_argument( '--release', dest='build_mode', action='store_const', const='release', default='debug', help='Build the PARSER_APK. in Release mode') parser_apk.add_argument( '--use-setup-py', dest="use_setup_py", action='store_true', default=False, help="Process the setup.py of a project if present. " + "(Experimental!") parser_apk.add_argument( '--ignore-setup-py', dest="ignore_setup_py", action='store_true', default=False, help="Don't run the setup.py of a project if present. " + "This may be required if the setup.py is not " + "designed to work inside p4a (e.g. by installing " + "dependencies that won't work or aren't desired " + "on Android") parser_apk.add_argument( '--keystore', dest='keystore', action='store', default=None, help=('Keystore for JAR signing key, will use jarsigner ' 'default if not specified (release build only)')) parser_apk.add_argument( '--signkey', dest='signkey', action='store', default=None, help='Key alias to sign PARSER_APK. with (release build only)') parser_apk.add_argument( '--keystorepw', dest='keystorepw', action='store', default=None, help='Password for keystore') parser_apk.add_argument( '--signkeypw', dest='signkeypw', action='store', default=None, help='Password for key alias') add_parser( subparsers, 'create', help='Compile a set of requirements into a dist', parents=[generic_parser]) add_parser( subparsers, 'archs', help='List the available target architectures', parents=[generic_parser]) add_parser( subparsers, 'distributions', aliases=['dists'], help='List the currently available (compiled) dists', parents=[generic_parser]) add_parser( subparsers, 'delete_dist', aliases=['delete-dist'], help='Delete a compiled dist', parents=[generic_parser]) parser_sdk_tools = add_parser( subparsers, 'sdk_tools', aliases=['sdk-tools'], help='Run the given binary from the SDK tools dis', parents=[generic_parser]) parser_sdk_tools.add_argument( 'tool', help='The binary tool name to run') add_parser( subparsers, 'adb', help='Run adb from the given SDK', parents=[generic_parser]) add_parser( subparsers, 'logcat', help='Run logcat from the given SDK', parents=[generic_parser]) add_parser( subparsers, 'build_status', aliases=['build-status'], help='Print some debug information about current built components', parents=[generic_parser]) parser.add_argument('-v', '--version', action='version', version=__version__) args, unknown = parser.parse_known_args(sys.argv[1:]) args.unknown_args = unknown if hasattr(args, "private") and args.private is not None: # Pass this value on to the internal bootstrap build.py: args.unknown_args += ["--private", args.private] if hasattr(args, "ignore_setup_py") and args.ignore_setup_py: args.use_setup_py = False self.args = args if args.subparser_name is None: parser.print_help() exit(1) setup_color(args.color) if args.debug: logger.setLevel(logging.DEBUG) self.ctx = Context() self.ctx.use_setup_py = getattr(args, "use_setup_py", True) have_setup_py_or_similar = False if getattr(args, "private", None) is not None: project_dir = getattr(args, "private") if (os.path.exists(os.path.join(project_dir, "setup.py")) or os.path.exists(os.path.join(project_dir, "pyproject.toml"))): have_setup_py_or_similar = True # Process requirements and put version in environ if hasattr(args, 'requirements'): requirements = [] # Add dependencies from setup.py, but only if they are recipes # (because otherwise, setup.py itself will install them later) if (have_setup_py_or_similar and getattr(args, "use_setup_py", False)): try: info("Analyzing package dependencies. MAY TAKE A WHILE.") # Get all the dependencies corresponding to a recipe: dependencies = [ dep.lower() for dep in get_dep_names_of_package( args.private, keep_version_pins=True, recursive=True, verbose=True, ) ] info("Dependencies obtained: " + str(dependencies)) all_recipes = [ recipe.lower() for recipe in set(Recipe.list_recipes(self.ctx)) ] dependencies = set(dependencies).intersection( set(all_recipes) ) # Add dependencies to argument list: if len(dependencies) > 0: if len(args.requirements) > 0: args.requirements += u"," args.requirements += u",".join(dependencies) except ValueError: # Not a python package, apparently. warning( "Processing failed, is this project a valid " "package? Will continue WITHOUT setup.py deps." ) # Parse --requirements argument list: for requirement in split_argument_list(args.requirements): if "==" in requirement: requirement, version = requirement.split(u"==", 1) os.environ["VERSION_{}".format(requirement)] = version info('Recipe {}: version "{}" requested'.format( requirement, version)) requirements.append(requirement) args.requirements = u",".join(requirements) self.warn_on_deprecated_args(args) self.storage_dir = args.storage_dir self.ctx.setup_dirs(self.storage_dir) self.sdk_dir = args.sdk_dir self.ndk_dir = args.ndk_dir self.android_api = args.android_api self.ndk_api = args.ndk_api self.ctx.symlink_java_src = args.symlink_java_src self.ctx.java_build_tool = args.java_build_tool self._archs = split_argument_list(args.arch) self.ctx.local_recipes = args.local_recipes self.ctx.copy_libs = args.copy_libs # Each subparser corresponds to a method getattr(self, args.subparser_name.replace('-', '_'))(args)
def __init__(self): parser = argparse.ArgumentParser( description="Tool for managing the Android / Python toolchain", usage="""toolchain <command> [<args>] Available commands: adb Runs adb binary from the detected SDK dir apk Create an APK using the given distribution bootstraps List all the bootstraps available to build with. build_status Informations about the current build create Build an android project with all recipes clean_all Delete all build components clean_builds Delete all build caches clean_dists Delete all compiled distributions clean_download_cache Delete any downloaded recipe packages clean_recipe_build Delete the build files of a recipe distributions List all distributions export_dist Copies a created dist to an output directory logcat Runs logcat from the detected SDK dir print_context_info Prints debug informations recipes List all the available recipes sdk_tools Runs android binary from the detected SDK dir symlink_dist Symlinks a created dist to an output directory Planned commands: build_dist """) parser.add_argument("command", help="Command to run") # General options parser.add_argument( '--debug', dest='debug', action='store_true', help='Display debug output and all build info') parser.add_argument( '--sdk-dir', '--sdk_dir', dest='sdk_dir', default='', help='The filepath where the Android SDK is installed') parser.add_argument( '--ndk-dir', '--ndk_dir', dest='ndk_dir', default='', help='The filepath where the Android NDK is installed') parser.add_argument( '--android-api', '--android_api', dest='android_api', default=0, type=int, help='The Android API level to build against.') parser.add_argument( '--ndk-version', '--ndk_version', dest='ndk_version', default='', help=('The version of the Android NDK. This is optional, ' 'we try to work it out automatically from the ndk_dir.')) parser.add_argument( '--storage-dir', dest='storage_dir', default=self.default_storage_dir, help=('Primary storage directory for downloads and builds ' '(default: {})'.format(self.default_storage_dir))) # AND: This option doesn't really fit in the other categories, the # arg structure needs a rethink parser.add_argument( '--arch', help='The archs to build for, separated by commas.', default='armeabi') # Options for specifying the Distribution parser.add_argument( '--dist-name', '--dist_name', help='The name of the distribution to use or create', default='') parser.add_argument( '--requirements', help=('Dependencies of your app, should be recipe names or ' 'Python modules'), default='') add_boolean_option( parser, ["allow-download"], default=False, description='Whether to allow binary dist download:') add_boolean_option( parser, ["allow-build"], default=True, description='Whether to allow compilation of a new distribution:') add_boolean_option( parser, ["force-build"], default=False, description='Whether to force compilation of a new distribution:') parser.add_argument( '--extra-dist-dirs', '--extra_dist_dirs', dest='extra_dist_dirs', default='', help='Directories in which to look for distributions') add_boolean_option( parser, ["require-perfect-match"], default=False, description=('Whether the dist recipes must perfectly match ' 'those requested')) parser.add_argument( '--local-recipes', '--local_recipes', dest='local_recipes', default='./p4a-recipes', help='Directory to look for local recipes') add_boolean_option( parser, ['copy-libs'], default=False, description='Copy libraries instead of using biglink (Android 4.3+)') self._read_configuration() args, unknown = parser.parse_known_args(sys.argv[1:]) self.dist_args = args # strip version from requirements, and put them in environ requirements = [] for requirement in split_argument_list(args.requirements): if "==" in requirement: requirement, version = requirement.split(u"==", 1) os.environ["VERSION_{}".format(requirement)] = version info('Recipe {}: version "{}" requested'.format( requirement, version)) requirements.append(requirement) args.requirements = u",".join(requirements) if args.debug: logger.setLevel(logging.DEBUG) self.ctx = Context() self.storage_dir = args.storage_dir self.ctx.setup_dirs(self.storage_dir) self.sdk_dir = args.sdk_dir self.ndk_dir = args.ndk_dir self.android_api = args.android_api self.ndk_version = args.ndk_version self._archs = split_argument_list(args.arch) # AND: Fail nicely if the args aren't handled yet if args.extra_dist_dirs: warning('Received --extra_dist_dirs but this arg currently is not ' 'handled, exiting.') exit(1) if args.allow_download: warning('Received --allow_download but this arg currently is not ' 'handled, exiting.') exit(1) # if args.allow_build: # warning('Received --allow_build but this arg currently is not ' # 'handled, exiting.') # exit(1) if not hasattr(self, args.command): print('Unrecognized command') parser.print_help() exit(1) self.ctx.local_recipes = args.local_recipes self.ctx.copy_libs = args.copy_libs getattr(self, args.command)(unknown)
def __init__(self): argv = sys.argv # Buildozer used to pass these arguments in a now-invalid order # If that happens, apply this fix # This fix will be removed once a fixed buildozer is released if (len(argv) > 2 and argv[1].startswith('--color') and argv[2].startswith('--storage-dir')): argv.append(argv.pop(1)) # the --color arg argv.append(argv.pop(1)) # the --storage-dir arg parser = NoAbbrevParser( description=('A packaging tool for turning Python scripts and apps ' 'into Android APKs')) generic_parser = argparse.ArgumentParser( add_help=False, description=('Generic arguments applied to all commands')) dist_parser = argparse.ArgumentParser( add_help=False, description=('Arguments for dist building')) generic_parser.add_argument( '--debug', dest='debug', action='store_true', default=False, help='Display debug output and all build info') generic_parser.add_argument( '--color', dest='color', choices=['always', 'never', 'auto'], help='Enable or disable color output (default enabled on tty)') generic_parser.add_argument( '--sdk-dir', '--sdk_dir', dest='sdk_dir', default='', help='The filepath where the Android SDK is installed') generic_parser.add_argument( '--ndk-dir', '--ndk_dir', dest='ndk_dir', default='', help='The filepath where the Android NDK is installed') generic_parser.add_argument( '--android-api', '--android_api', dest='android_api', default=0, type=int, help='The Android API level to build against.') generic_parser.add_argument( '--ndk-version', '--ndk_version', dest='ndk_version', default='', help=('The version of the Android NDK. This is optional, ' 'we try to work it out automatically from the ndk_dir.')) generic_parser.add_argument( '--symlink-java-src', '--symlink_java_src', action='store_true', dest='symlink_java_src', default=False, help=('If True, symlinks the java src folder during build and dist ' 'creation. This is useful for development only, it could also ' 'cause weird problems.')) default_storage_dir = user_data_dir('python-for-android') if ' ' in default_storage_dir: default_storage_dir = '~/.python-for-android' generic_parser.add_argument( '--storage-dir', dest='storage_dir', default=default_storage_dir, help=('Primary storage directory for downloads and builds ' '(default: {})'.format(default_storage_dir))) generic_parser.add_argument( '--arch', help='The archs to build for, separated by commas.', default='armeabi') # Options for specifying the Distribution generic_parser.add_argument( '--dist-name', '--dist_name', help='The name of the distribution to use or create', default='') generic_parser.add_argument( '--requirements', help=('Dependencies of your app, should be recipe names or ' 'Python modules'), default='') generic_parser.add_argument( '--bootstrap', help='The bootstrap to build with. Leave unset to choose automatically.', default=None) generic_parser.add_argument( '--hook', help='Filename to a module that contain python-for-android hooks', default=None) add_boolean_option( generic_parser, ["force-build"], default=False, description='Whether to force compilation of a new distribution:') add_boolean_option( generic_parser, ["require-perfect-match"], default=False, description=('Whether the dist recipes must perfectly match ' 'those requested')) generic_parser.add_argument( '--local-recipes', '--local_recipes', dest='local_recipes', default='./p4a-recipes', help='Directory to look for local recipes') generic_parser.add_argument( '--java-build-tool', dest='java_build_tool', default='auto', choices=['auto', 'ant', 'gradle'], help=('The java build tool to use when packaging the APK, defaults ' 'to automatically selecting an appropriate tool.')) add_boolean_option( generic_parser, ['copy-libs'], default=False, description='Copy libraries instead of using biglink (Android 4.3+)') self._read_configuration() subparsers = parser.add_subparsers(dest='subparser_name', help='The command to run') def add_parser(subparsers, *args, **kwargs): ''' argparse in python2 doesn't support the aliases option, so we just don't provide the aliases there. ''' if 'aliases' in kwargs and sys.version_info.major < 3: kwargs.pop('aliases') return subparsers.add_parser(*args, **kwargs) parser_recipes = add_parser(subparsers, 'recipes', parents=[generic_parser], help='List the available recipes') parser_recipes.add_argument( "--compact", action="store_true", default=False, help="Produce a compact list suitable for scripting") parser_bootstraps = add_parser( subparsers, 'bootstraps', help='List the available bootstraps', parents=[generic_parser]) parser_clean_all = add_parser( subparsers, 'clean_all', aliases=['clean-all'], help='Delete all builds, dists and caches', parents=[generic_parser]) parser_clean_dists = add_parser( subparsers, 'clean_dists', aliases=['clean-dists'], help='Delete all dists', parents=[generic_parser]) parser_clean_bootstrap_builds = add_parser( subparsers, 'clean_bootstrap_builds', aliases=['clean-bootstrap-builds'], help='Delete all bootstrap builds', parents=[generic_parser]) parser_clean_builds = add_parser( subparsers, 'clean_builds', aliases=['clean-builds'], help='Delete all builds', parents=[generic_parser]) parser_clean = add_parser(subparsers, 'clean', help='Delete build components.', parents=[generic_parser]) parser_clean.add_argument( 'component', nargs='+', help=('The build component(s) to delete. You can pass any ' 'number of arguments from "all", "builds", "dists", ' '"distributions", "bootstrap_builds", "downloads".')) parser_clean_recipe_build = add_parser(subparsers, 'clean_recipe_build', aliases=['clean-recipe-build'], help=('Delete the build components of the given recipe. ' 'By default this will also delete built dists'), parents=[generic_parser]) parser_clean_recipe_build.add_argument('recipe', help='The recipe name') parser_clean_recipe_build.add_argument('--no-clean-dists', default=False, dest='no_clean_dists', action='store_true', help='If passed, do not delete existing dists') parser_clean_download_cache= add_parser(subparsers, 'clean_download_cache', aliases=['clean-download-cache'], help='Delete cached downloads for requirement builds', parents=[generic_parser]) parser_clean_download_cache.add_argument( 'recipes', nargs='*', help=('The recipes to clean (space-separated). If no recipe name is ' 'provided, the entire cache is cleared.')) parser_export_dist = add_parser(subparsers, 'export_dist', aliases=['export-dist'], help='Copy the named dist to the given path', parents=[generic_parser]) parser_export_dist.add_argument('output_dir', help=('The output dir to copy to')) parser_export_dist.add_argument('--symlink', action='store_true', help=('Symlink the dist instead of copying')) parser_apk = add_parser(subparsers, 'apk', help='Build an APK', parents=[generic_parser]) parser_apk.add_argument('--release', dest='build_mode', action='store_const', const='release', default='debug', help='Build the PARSER_APK. in Release mode') parser_apk.add_argument('--keystore', dest='keystore', action='store', default=None, help=('Keystore for JAR signing key, will use jarsigner ' 'default if not specified (release build only)')) parser_apk.add_argument('--signkey', dest='signkey', action='store', default=None, help='Key alias to sign PARSER_APK. with (release build only)') parser_apk.add_argument('--keystorepw', dest='keystorepw', action='store', default=None, help='Password for keystore') parser_apk.add_argument('--signkeypw', dest='signkeypw', action='store', default=None, help='Password for key alias') parser_create = add_parser(subparsers, 'create', help='Compile a set of requirements into a dist', parents=[generic_parser]) parser_archs = add_parser(subparsers, 'archs', help='List the available target architectures', parents=[generic_parser]) parser_distributions = add_parser(subparsers, 'distributions', aliases=['dists'], help='List the currently available (compiled) dists', parents=[generic_parser]) parser_delete_dist = add_parser(subparsers, 'delete_dist', aliases=['delete-dist'], help='Delete a compiled dist', parents=[generic_parser]) parser_sdk_tools = add_parser(subparsers, 'sdk_tools', aliases=['sdk-tools'], help='Run the given binary from the SDK tools dis', parents=[generic_parser]) parser_sdk_tools.add_argument( 'tool', help=('The tool binary name to run')) parser_adb = add_parser(subparsers, 'adb', help='Run adb from the given SDK', parents=[generic_parser]) parser_logcat = add_parser(subparsers, 'logcat', help='Run logcat from the given SDK', parents=[generic_parser]) parser_build_status = add_parser(subparsers, 'build_status', aliases=['build-status'], help='Print some debug information about current built components', parents=[generic_parser]) parser.add_argument('-v', '--version', action='version', version=__version__) args, unknown = parser.parse_known_args(sys.argv[1:]) args.unknown_args = unknown self.args = args if args.subparser_name is None: parser.print_help() exit(1) setup_color(args.color) if args.debug: logger.setLevel(logging.DEBUG) # strip version from requirements, and put them in environ if hasattr(args, 'requirements'): requirements = [] for requirement in split_argument_list(args.requirements): if "==" in requirement: requirement, version = requirement.split(u"==", 1) os.environ["VERSION_{}".format(requirement)] = version info('Recipe {}: version "{}" requested'.format( requirement, version)) requirements.append(requirement) args.requirements = u",".join(requirements) self.ctx = Context() self.storage_dir = args.storage_dir self.ctx.setup_dirs(self.storage_dir) self.sdk_dir = args.sdk_dir self.ndk_dir = args.ndk_dir self.android_api = args.android_api self.ndk_version = args.ndk_version self.ctx.symlink_java_src = args.symlink_java_src self.ctx.java_build_tool = args.java_build_tool self._archs = split_argument_list(args.arch) self.ctx.local_recipes = args.local_recipes self.ctx.copy_libs = args.copy_libs # Each subparser corresponds to a method getattr(self, args.subparser_name.replace('-', '_'))(args)