From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga05.intel.com (mga05.intel.com []) by mx.groups.io with SMTP id smtpd.web12.1673.1585780505388364153 for ; Wed, 01 Apr 2020 15:35:05 -0700 Authentication-Results: mx.groups.io; dkim=missing; spf=fail (domain: intel.com, ip: , mailfrom: nathaniel.l.desimone@intel.com) IronPort-SDR: j5EmcYteV+qF1twdpLiGizupvL2Vds87pDQOYvJdRTkPkO5UxurWGA1tQIaAUgf1j0/vfUVmjw +QmKzEQ8bUXw== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 01 Apr 2020 15:35:05 -0700 IronPort-SDR: iy1WYjC6yuuhdxQ/3eyjSlz4rrY7Bh/uRm+5PelZPnfAGJioMaMQ/oILw/lwuj1p5iHUP/2Fdf TQGNJmc+kiZA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.72,333,1580803200"; d="scan'208";a="249615992" Received: from nldesimo-desk1.amr.corp.intel.com ([10.7.159.63]) by orsmga003.jf.intel.com with ESMTP; 01 Apr 2020 15:35:04 -0700 From: "Nate DeSimone" To: devel@edk2.groups.io Cc: Ashley DeSimone , Puja Pandya , Erik Bjorge , Prince Agyeman , Bret Barkelew , Philippe Mathieu-Daude Subject: [edk2-staging/EdkRepo] [PATCH V1 1/3] EdkRepo: Generate command completion scripts Date: Wed, 1 Apr 2020 15:34:50 -0700 Message-Id: <20200401223452.4805-2-nathaniel.l.desimone@intel.com> X-Mailer: git-send-email 2.24.0.windows.2 In-Reply-To: <20200401223452.4805-1-nathaniel.l.desimone@intel.com> References: <20200401223452.4805-1-nathaniel.l.desimone@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Adds code to edkrepo_cli.py to generate a bash/zsh compatible command completion script. Add a new command_completion_edkrepo.py script which is callable by the shell when needed for dynamic completion data. For example, providing completion for "edkrepo checkout" requires the shell to know the list of possible branch combinations, this requires parsing the manifest XML. command_completion_edkrepo.py provides a means for the shell to get that type of data. Cc: Ashley DeSimone Cc: Puja Pandya Cc: Erik Bjorge Cc: Prince Agyeman Cc: Bret Barkelew Cc: Philippe Mathieu-Daude Signed-off-by: Nate DeSimone --- edkrepo/command_completion_edkrepo.py | 86 +++++++++++++++++++++++++++ edkrepo/edkrepo_cli.py | 61 ++++++++++++++++++- setup.py | 8 +-- 3 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 edkrepo/command_completion_edkrepo.py diff --git a/edkrepo/command_completion_edkrepo.py b/edkrepo/command_completion_edkrepo.py new file mode 100644 index 0000000..05de9ca --- /dev/null +++ b/edkrepo/command_completion_edkrepo.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# +## @file +# command_completion_edkrepo.py +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import argparse +import io +import os +import sys +import traceback + +from edkrepo_manifest_parser.edk_manifest import ManifestXml +from edkrepo.config import config_factory +from edkrepo.config.config_factory import get_workspace_manifest + +def checkout(parsed_args, config): + manifest = get_workspace_manifest() + print(' '.join([c.name for c in manifest.combinations])) + +def current_combo(parsed_args, config): + manifest = get_workspace_manifest() + print(" [{}]".format(manifest.general_config.current_combo)) + +def checkout_pin(parsed_args, config): + pins = [] + manifest_directory = config['cfg_file'].manifest_repo_abs_local_path + manifest = get_workspace_manifest() + pin_folder = os.path.normpath(os.path.join(manifest_directory, manifest.general_config.pin_path)) + for dirpath, _, filenames in os.walk(pin_folder): + for file in filenames: + pin_file = os.path.join(dirpath, file) + # Capture error output from manifest parser stdout so it is hidden unless verbose is enabled + stdout = sys.stdout + sys.stdout = io.StringIO() + pin = ManifestXml(pin_file) + parse_output = sys.stdout.getvalue() + sys.stdout = stdout + if parsed_args.verbose and parse_output.strip() != '': + print('Pin {} Parsing Errors: {}\n'.format(file, parse_output.strip())) + if pin.project_info.codename == manifest.project_info.codename: + pins.append(file) + print(' '.join(pins)) + +# To add command completions for a new command, add an entry to this dictionary. +command_completions = { + 'current-combo': current_combo, + 'checkout': checkout, + 'checkout-pin': checkout_pin, + 'chp': checkout_pin +} + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-v", "--verbose", action="store_true", help='Increases command verbosity') + subparsers = parser.add_subparsers(dest='subparser_name') + for command_completion in command_completions: + subparsers.add_parser(command_completion, formatter_class=argparse.RawTextHelpFormatter) + if len(sys.argv) <= 1: + return 0 + parsed_args = parser.parse_args() + try: + command_name = parsed_args.subparser_name + config = {} + config["cfg_file"] = config_factory.GlobalConfig() + config["user_cfg_file"] = config_factory.GlobalUserConfig() + if command_name not in command_completions: + return 1 + command_completions[command_name](parsed_args, config) + return 0 + except Exception as e: + if parsed_args.verbose: + traceback.print_exc() + print("Error: {}".format(str(e))) + return 1 + return 0 + +if __name__ == "__main__": + try: + sys.exit(main()) + except Exception as e: + traceback.print_exc() + sys.exit(1) diff --git a/edkrepo/edkrepo_cli.py b/edkrepo/edkrepo_cli.py index 0b69860..03061c9 100644 --- a/edkrepo/edkrepo_cli.py +++ b/edkrepo/edkrepo_cli.py @@ -3,7 +3,7 @@ ## @file # edkrepo_cli.py # -# Copyright (c) 2017- 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -29,6 +29,7 @@ from edkrepo.common.edkrepo_exception import EdkrepoException, EdkrepoGlobalConf from edkrepo.common.edkrepo_exception import EdkrepoWarningException from edkrepo.common.edkrepo_exception import EdkrepoConfigFileInvalidException from edkrepo.common.humble import KEYBOARD_INTERRUPT, GIT_CMD_ERROR +from edkrepo.common.pathfix import get_actual_path def generate_command_line(command): parser = argparse.ArgumentParser() @@ -100,6 +101,61 @@ def generate_command_line(command): subparser_name.add_argument(('--' + arg.get('name')), action=arg_action, help=arg.get('help-text')) return parser +command_completion_script_header='''#!/usr/bin/env bash +# +## @file edkrepo_completions.sh +# +# Automatically generated please DO NOT modify !!! +# + +''' +def generate_command_completion_script(script_filename, parser): + import edkrepo.command_completion_edkrepo as completion + commands = [] + for action in parser._positionals._group_actions: + if action.choices is not None: + commands = [c for c in action.choices] + break + commands = sorted(commands) + commands_with_3rd_param_completion = [c for c in completion.command_completions if c in commands] + commands_with_3rd_param_completion = sorted(commands_with_3rd_param_completion) + with open(script_filename, 'w') as f: + f.write(command_completion_script_header) + if sys.platform == "win32": + command_completion_path = os.path.dirname(sys.executable) + command_completion_path = os.path.join(command_completion_path, 'Scripts', "command_completion_edkrepo.exe") + if not os.path.isfile(command_completion_path): + print('command_completion_edkrepo.exe not found') + return + command_completion_path = get_actual_path(command_completion_path) + (drive, path) = os.path.splitdrive(command_completion_path) + command_completion_path = '/{}{}'.format(drive.replace(':','').lower(), path.replace('\\','/')) + f.write("export command_completion_edkrepo_file='{}'\n".format(command_completion_path)) + f.write('alias command_completion_edkrepo="$command_completion_edkrepo_file"\n') + f.write('_edkrepo_completions() {\n if [ "${#COMP_WORDS[@]}" -eq "2" ]; then\n') + f.write(' COMPREPLY=($(compgen -W "{}" -- "${{COMP_WORDS[1]}}"))\n'.format(' '.join(commands))) + if len(commands_with_3rd_param_completion) > 0: + f.write(' elif [ "${#COMP_WORDS[@]}" -eq "3" ]; then\n') + first_loop = True + for command in commands_with_3rd_param_completion: + if first_loop: + f.write(' if [ "${{COMP_WORDS[1]}}" == "{}" ]; then\n'.format(command)) + first_loop = False + else: + f.write(' elif [ "${{COMP_WORDS[1]}}" == "{}" ]; then\n'.format(command)) + f.write(' COMPREPLY=($(compgen -W "$(command_completion_edkrepo ${COMP_WORDS[1]})" -- "${COMP_WORDS[2]}"))\n') + if len(commands_with_3rd_param_completion) > 0: + f.write(' fi\n') + f.write(' fi\n}\n\n') + if len(commands_with_3rd_param_completion) > 0: + if sys.platform == "win32": + f.write('if [ -x "$(command -v edkrepo)" ] && [ -x "$(command -v $command_completion_edkrepo_file)" ]; then\n') + else: + f.write('if [ -x "$(command -v edkrepo)" ] && [ -x "$(command -v command_completion_edkrepo)" ]; then\n') + else: + f.write('if [ -x "$(command -v edkrepo)" ]; then\n') + f.write(' complete -F _edkrepo_completions edkrepo\nfi\n') + def main(): command = command_factory.create_composite_command() config = {} @@ -117,6 +173,9 @@ def main(): if len(sys.argv) <= 1: parser.print_help() return 1 + if sys.argv[1] == 'generate-command-completion-script' and len(sys.argv) >= 3: + generate_command_completion_script(sys.argv[2], parser) + return 0 parsed_args = parser.parse_args() command_name = parsed_args.subparser_name try: diff --git a/setup.py b/setup.py index e14aed1..e7e6ce8 100755 --- a/setup.py +++ b/setup.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 # -## @file -# setup.py +## @file setup.py # -# Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -20,7 +19,8 @@ setup(name='edkrepo', include_package_data=True, entry_points={ 'console_scripts': [ - 'edkrepo = edkrepo.edkrepo_entry_point:main' + 'edkrepo = edkrepo.edkrepo_entry_point:main', + 'command_completion_edkrepo = edkrepo.command_completion_edkrepo:main' ] } ) -- 2.24.0.windows.2