From def990cb7b31ed30cb4b2905d5a9d6f489207607 Mon Sep 17 00:00:00 2001 From: kyleknap Date: Wed, 30 Jun 2021 20:27:04 -0700 Subject: [PATCH] Make updates for logic to work on Windows Specifically: * Handle problematic characters such as spaces by quoting arguments * Add compatibilty shims for differences in virtualenv and executable names We should also consider making these additional improvements: * See if we can add a compatibilty shim to make the default prefix on Windows C:\Program Files\aws-cli in order to make a more sane Windows default. * Consider copying over the aws_completer to the bin folder. This is not too useful for Windows since it does not use bash. It would also require making a .cmd file for the completer. --- Makefile.in | 22 ++++++------ bin/aws.cmd | 32 +----------------- bin/aws_bash_completer | 6 ---- bin/aws_legacy_completer | 29 ---------------- bin/aws_zsh_completer.sh | 60 --------------------------------- scripts/buildctl/awscli_venv.py | 31 ++++++++++++----- scripts/buildctl/constants.py | 4 +++ scripts/buildctl/install.py | 23 ++++++++++--- setup.cfg | 4 --- 9 files changed, 56 insertions(+), 155 deletions(-) delete mode 100644 bin/aws_bash_completer delete mode 100755 bin/aws_legacy_completer delete mode 100644 bin/aws_zsh_completer.sh diff --git a/Makefile.in b/Makefile.in index 8f648f9fe2c4..7c4e10daee70 100644 --- a/Makefile.in +++ b/Makefile.in @@ -16,25 +16,25 @@ DOWNLOAD_DEPS_FLAG = @DOWNLOAD_DEPS_FLAG@ all: build build: - $(PYTHON) $(srcdir)/scripts/buildctl \ + "$(PYTHON)" "$(srcdir)/scripts/buildctl" \ build \ - --artifact $(INSTALL_TYPE) \ - --build-dir $(aws_cli_builddir) $(DOWNLOAD_DEPS_FLAG) + --artifact "$(INSTALL_TYPE)" \ + --build-dir "$(aws_cli_builddir)" $(DOWNLOAD_DEPS_FLAG) clean: - rm -rf $(aws_cli_builddir) + rm -rf "$(aws_cli_builddir)" install: - $(PYTHON) $(srcdir)/scripts/buildctl \ + "$(PYTHON)" "$(srcdir)/scripts/buildctl" \ install \ - --build-dir $(aws_cli_builddir) \ - --lib-dir $(libdir) \ - --bin-dir $(bindir) + --build-dir "$(aws_cli_builddir)" \ + --lib-dir "$(libdir)" \ + --bin-dir "$(bindir)" uninstall: - $(PYTHON) $(srcdir)/scripts/buildctl \ + "$(PYTHON)" "$(srcdir)/scripts/buildctl" \ uninstall \ - --lib-dir $(libdir) \ - --bin-dir $(bindir) + --lib-dir "$(libdir)" \ + --bin-dir "$(bindir)" .PHONY: all build install uninstall diff --git a/bin/aws.cmd b/bin/aws.cmd index 3f8caa78af44..e213f33946e1 100644 --- a/bin/aws.cmd +++ b/bin/aws.cmd @@ -1,34 +1,4 @@ -@echo OFF -REM=""" -setlocal -set PythonExe="" -set PythonExeFlags= - -for %%i in (cmd bat exe) do ( - for %%j in (python.%%i) do ( - call :SetPythonExe "%%~$PATH:j" - ) -) -for /f "tokens=2 delims==" %%i in ('assoc .py') do ( - for /f "tokens=2 delims==" %%j in ('ftype %%i') do ( - for /f "tokens=1" %%k in ("%%j") do ( - call :SetPythonExe %%k - ) - ) -) -%PythonExe% -x %PythonExeFlags% "%~f0" %* -exit /B %ERRORLEVEL% -goto :EOF - -:SetPythonExe -if not ["%~1"]==[""] ( - if [%PythonExe%]==[""] ( - set PythonExe="%~1" - ) -) -goto :EOF -""" - +@echo off & python -x "%~f0" %* & goto :eof # =================================================== # Python script starts here # =================================================== diff --git a/bin/aws_bash_completer b/bin/aws_bash_completer deleted file mode 100644 index 32cd1dd8fd49..000000000000 --- a/bin/aws_bash_completer +++ /dev/null @@ -1,6 +0,0 @@ -# Typically that would be added under one of the following paths: -# - /etc/bash_completion.d -# - /usr/local/etc/bash_completion.d -# - /usr/share/bash-completion/completions - -complete -C aws_completer aws diff --git a/bin/aws_legacy_completer b/bin/aws_legacy_completer deleted file mode 100755 index dba52dd56c5e..000000000000 --- a/bin/aws_legacy_completer +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved. - -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at - -# http://aws.amazon.com/apache2.0/ - -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. - -import os -if os.environ.get('LC_CTYPE', '') == 'UTF-8': - os.environ['LC_CTYPE'] = 'en_US.UTF-8' -import awscli.completer - -if __name__ == '__main__': - # bash exports COMP_LINE and COMP_POINT, tcsh COMMAND_LINE only - cline = os.environ.get('COMP_LINE') or os.environ.get('COMMAND_LINE') or '' - cpoint = int(os.environ.get('COMP_POINT') or len(cline)) - try: - awscli.completer.complete(cline, cpoint) - except KeyboardInterrupt: - # If the user hits Ctrl+C, we don't want to print - # a traceback to the user. - pass diff --git a/bin/aws_zsh_completer.sh b/bin/aws_zsh_completer.sh deleted file mode 100644 index a2ee80e73974..000000000000 --- a/bin/aws_zsh_completer.sh +++ /dev/null @@ -1,60 +0,0 @@ -# Source this file to activate auto completion for zsh using the bash -# compatibility helper. Make sure to run `compinit` before, which should be -# given usually. -# -# % source /path/to/zsh_complete.sh -# -# Typically that would be called somewhere in your .zshrc. -# -# Note, the overwrite of _bash_complete() is to export COMP_LINE and COMP_POINT -# That is only required for zsh <= edab1d3dbe61da7efe5f1ac0e40444b2ec9b9570 -# -# https://github.com/zsh-users/zsh/commit/edab1d3dbe61da7efe5f1ac0e40444b2ec9b9570 -# -# zsh relases prior to that version do not export the required env variables! - -autoload -Uz bashcompinit -bashcompinit -i - -_bash_complete() { - local ret=1 - local -a suf matches - local -x COMP_POINT COMP_CWORD - local -a COMP_WORDS COMPREPLY BASH_VERSINFO - local -x COMP_LINE="$words" - local -A savejobstates savejobtexts - - (( COMP_POINT = 1 + ${#${(j. .)words[1,CURRENT]}} + $#QIPREFIX + $#IPREFIX + $#PREFIX )) - (( COMP_CWORD = CURRENT - 1)) - COMP_WORDS=( $words ) - BASH_VERSINFO=( 2 05b 0 1 release ) - - savejobstates=( ${(kv)jobstates} ) - savejobtexts=( ${(kv)jobtexts} ) - - [[ ${argv[${argv[(I)nospace]:-0}-1]} = -o ]] && suf=( -S '' ) - - matches=( ${(f)"$(compgen $@ -- ${words[CURRENT]})"} ) - - if [[ -n $matches ]]; then - if [[ ${argv[${argv[(I)filenames]:-0}-1]} = -o ]]; then - compset -P '*/' && matches=( ${matches##*/} ) - compset -S '/*' && matches=( ${matches%%/*} ) - compadd -Q -f "${suf[@]}" -a matches && ret=0 - else - compadd -Q "${suf[@]}" -a matches && ret=0 - fi - fi - - if (( ret )); then - if [[ ${argv[${argv[(I)default]:-0}-1]} = -o ]]; then - _default "${suf[@]}" && ret=0 - elif [[ ${argv[${argv[(I)dirnames]:-0}-1]} = -o ]]; then - _directories "${suf[@]}" && ret=0 - fi - fi - - return ret -} - -complete -C aws_completer aws diff --git a/scripts/buildctl/awscli_venv.py b/scripts/buildctl/awscli_venv.py index cf6dfe548c3e..a733ab4cc00d 100644 --- a/scripts/buildctl/awscli_venv.py +++ b/scripts/buildctl/awscli_venv.py @@ -46,20 +46,33 @@ def archive(self): @property def bin_dir(self): - return os.path.join(self._venv_dir, 'bin') + if sys.platform == 'win32': + return os.path.join(self._venv_dir, 'Scripts') + else: + return os.path.join(self._venv_dir, 'bin') @property def python_exe(self): - return os.path.join(self.bin_dir, 'python') + exe_name = 'python' + if sys.platform == 'win32': + exe_name += '.exe' + return os.path.join(self.bin_dir, exe_name) def _pip_install(self, args): - subprocess.run( - [self.python_exe, '-m', 'pip', 'install'] + args, - check=True - ) + args = [self.python_exe, '-m', 'pip', 'install'] + args + run_kwargs = {'check': True} + if sys.platform == 'win32': + args = ' '.join(args) + run_kwargs['shell'] = True + subprocess.run(args, **run_kwargs) def _site_packages(self): # TODO: We should figure out a more programmatic way of finding this... - python_version = f"python{sys.version_info[0]}.{sys.version_info[1]}" - return os.path.join( - self._venv_dir, 'lib', python_version, 'site-packages') + if sys.platform == 'win32': + return os.path.join( + self._venv_dir, 'Lib', 'site-packages') + else: + python_version = ( + f"python{sys.version_info[0]}.{sys.version_info[1]}") + return os.path.join( + self._venv_dir, 'lib', python_version, 'site-packages') diff --git a/scripts/buildctl/constants.py b/scripts/buildctl/constants.py index 843fecf1bf2f..af83c4a88b4a 100644 --- a/scripts/buildctl/constants.py +++ b/scripts/buildctl/constants.py @@ -1,4 +1,5 @@ import os +import sys ROOT_DIR = os.path.dirname( @@ -29,4 +30,7 @@ AC_INDEX = os.path.join(ROOT_DIR, 'awscli', 'data', 'ac.index') INSTALL_DIRNAME = 'aws-cli' + CLI_EXECUTABLES = ['aws', 'aws_completer'] +if sys.platform == 'win32': + CLI_EXECUTABLES = ['aws.cmd'] diff --git a/scripts/buildctl/install.py b/scripts/buildctl/install.py index ec959d356b98..687835b7b5b2 100644 --- a/scripts/buildctl/install.py +++ b/scripts/buildctl/install.py @@ -1,5 +1,6 @@ import os import shutil +import sys from constants import CLI_EXECUTABLES @@ -32,21 +33,30 @@ def _copy_to_install_dir(build_dir, install_dir, artifact_type): shutil.rmtree(install_dir) shutil.copytree(build_lib, install_dir) if artifact_type == 'system-sandbox': - _update_venv_shebangs(install_dir) + _update_script_header(install_dir) -def _update_venv_shebangs(install_dir): +def _update_script_header(install_dir): + python_exe_name = 'python' + if sys.platform == 'win32': + python_exe_name = 'python.exe' python_exe_path = _get_install_bin_exe( - install_dir, 'python', 'system-sandbox') + install_dir, python_exe_name, 'system-sandbox') for exe in CLI_EXECUTABLES: exe_path = _get_install_bin_exe(install_dir, exe, 'system-sandbox') with open(exe_path) as f: lines = f.readlines() - lines[0] = f'#!{python_exe_path}\n' + lines[0] = _get_script_header(python_exe_path) with open(exe_path, 'w') as f: f.write(''.join(lines)) +def _get_script_header(python_exe_path): + if sys.platform == 'win32': + return f'@echo off & "{python_exe_path}" -x "%~f0" %* & goto :eof\n' + return f'#!{python_exe_path}\n' + + def _symlink_executables(install_dir, bin_dir, artifact_type): if not os.path.exists(bin_dir): os.makedirs(bin_dir) @@ -67,5 +77,8 @@ def _get_build_lib(build_dir, artifact_type): def _get_install_bin_exe(install_dir, exe, artifact_type): install_bin_dir = install_dir if artifact_type == 'system-sandbox': - install_bin_dir = os.path.join(install_dir, 'bin') + bin_dirname = 'bin' + if sys.platform == 'win32': + bin_dirname = 'Scripts' + install_bin_dir = os.path.join(install_dir, bin_dirname) return os.path.join(install_bin_dir, exe) diff --git a/setup.cfg b/setup.cfg index 694680f2e1f4..366e63059a11 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,11 +21,7 @@ classifiers = scripts = bin/aws bin/aws_completer - # Should probably remove bin/aws.cmd - bin/aws_legacy_completer - bin/aws_zsh_completer.sh - bin/aws_bash_completer packages = find: python_requires = >=3.7 include_package_data = True