Skip to content

Commit fe29994

Browse files
committed
Merge branch 'fix/specify_shell_in_exports' into 'master'
fix: explicitly set shell type in export.sh for bash and zsh Closes IDF-11137, IDF-11138, and IDFGH-13713 See merge request espressif/esp-idf!33558
2 parents c843c2c + 6f3241a commit fe29994

File tree

5 files changed

+53
-37
lines changed

5 files changed

+53
-37
lines changed

export.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,19 @@ fi
1919
# Attempt to identify the ESP-IDF directory
2020
idf_path="."
2121

22+
shell_type="detect"
23+
2224
# shellcheck disable=SC2128,SC2169,SC2039,SC3054,SC3028 # ignore array expansion warning
2325
if test -n "${BASH_SOURCE-}"
2426
then
2527
# shellcheck disable=SC3028,SC3054 # unreachable with 'dash'
2628
idf_path=$(dirname "${BASH_SOURCE[0]}")
29+
shell_type="bash"
2730
elif test -n "${ZSH_VERSION-}"
2831
then
2932
# shellcheck disable=SC2296 # ignore parameter starts with '{' because it's zsh
3033
idf_path=$(dirname "${(%):-%x}")
34+
shell_type="zsh"
3135
elif test -n "${IDF_PATH-}"
3236
then
3337
idf_path=$IDF_PATH
@@ -46,7 +50,7 @@ fi
4650
. "${idf_path}/tools/detect_python.sh"
4751

4852
# Evaluate the ESP-IDF environment set up by the activate.py script.
49-
idf_exports=$("$ESP_PYTHON" "${idf_path}/tools/activate.py" --export)
53+
idf_exports=$("$ESP_PYTHON" "${idf_path}/tools/activate.py" --export --shell $shell_type)
5054
eval "${idf_exports}"
5155
unset idf_path
5256
return 0

tools/export_utils/activate_venv.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def parse_arguments() -> argparse.Namespace:
2525
epilog='On Windows, run `python activate.py` to execute this script in the current terminal window.')
2626
parser.add_argument('-s', '--shell',
2727
metavar='SHELL',
28-
default=os.environ.get('ESP_IDF_SHELL', None),
28+
default=os.environ.get('ESP_IDF_SHELL', 'detect'),
2929
help='Explicitly specify shell to start. For example bash, zsh, powershell.exe, cmd.exe')
3030
parser.add_argument('-l', '--list',
3131
action='store_true',
@@ -38,6 +38,7 @@ def parse_arguments() -> argparse.Namespace:
3838
help=('Disable ANSI color escape sequences.'))
3939
parser.add_argument('-d', '--debug',
4040
action='store_true',
41+
default=bool(os.environ.get('ESP_IDF_EXPORT_DEBUG')),
4142
help=('Enable debug information.'))
4243
parser.add_argument('-q', '--quiet',
4344
action='store_true',
@@ -100,17 +101,21 @@ def get_idf_env() -> Dict[str,str]:
100101
def detect_shell(args: Any) -> str:
101102
import psutil
102103

103-
if args.shell is not None:
104+
if args.shell != 'detect':
105+
debug(f'Shell explicitly stated: "{args.shell}"')
104106
return str(args.shell)
105107

106108
current_pid = os.getpid()
107109
detected_shell_name = ''
108110
while True:
109111
parent_pid = psutil.Process(current_pid).ppid()
110-
parent_name = os.path.basename(psutil.Process(parent_pid).exe())
112+
parent = psutil.Process(parent_pid)
113+
parent_cmdline = parent.cmdline()
114+
parent_exe = parent_cmdline[0].lstrip('-')
115+
parent_name = os.path.basename(parent_exe)
116+
debug(f'Parent: pid: {parent_pid}, cmdline: {parent_cmdline}, exe: {parent_exe}, name: {parent_name}')
111117
if not parent_name.lower().startswith('python'):
112118
detected_shell_name = parent_name
113-
conf.DETECTED_SHELL_PATH = psutil.Process(parent_pid).exe()
114119
break
115120
current_pid = parent_pid
116121

@@ -143,6 +148,7 @@ def main() -> None:
143148
# Fill config global holder
144149
conf.ARGS = args
145150

151+
debug(f'command line: {sys.argv}')
146152
if conf.ARGS.list:
147153
oprint(SUPPORTED_SHELLS)
148154
sys.exit()

tools/export_utils/console_output.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
CONSOLE_STDOUT = Console(width=255)
1818

1919

20-
def status_message(msg: str, rv_on_ok: bool=False, die_on_err: bool=True) -> Callable:
20+
def status_message(msg: str, msg_result: str='', rv_on_ok: bool=False, die_on_err: bool=True) -> Callable:
2121
def inner(func: Callable) -> Callable:
2222
def wrapper(*args: Any, **kwargs: Any) -> Any:
2323
eprint(f'[dark_orange]*[/dark_orange] {msg} ... ', end='')
@@ -34,6 +34,8 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
3434

3535
if rv_on_ok:
3636
eprint(f'[green]{rv}[/green]')
37+
elif msg_result:
38+
eprint(f'[green]{msg_result}[/green]')
3739
else:
3840
eprint('[green]OK[/green]')
3941

tools/export_utils/shell_types.py

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
import shutil
66
import sys
7+
import textwrap
78
from pathlib import Path
89
from subprocess import run
910
from tempfile import gettempdir
@@ -72,10 +73,6 @@ def autocompletion(self) -> None:
7273
# Basic POSIX shells does not support autocompletion
7374
return None
7475

75-
def init_file(self) -> None:
76-
with open(self.script_file_path, 'w') as fd:
77-
self.export_file(fd)
78-
7976
def export_file(self, fd: TextIO) -> None:
8077
fd.write(f'{self.deactivate_cmd}\n')
8178
for var, value in self.new_esp_idf_env.items():
@@ -87,34 +84,32 @@ def export_file(self, fd: TextIO) -> None:
8784
'Go to the project directory and run:\n\n idf.py build"\n'))
8885

8986
def export(self) -> None:
90-
self.init_file()
87+
with open(self.script_file_path, 'w') as fd:
88+
self.export_file(fd)
9189
print(f'. {self.script_file_path}')
9290

9391
def click_ver(self) -> int:
9492
return int(click.__version__.split('.')[0])
9593

9694

9795
class BashShell(UnixShell):
98-
def get_bash_major_minor(self) -> float:
99-
env = self.expanded_env()
100-
bash_interpreter = conf.DETECTED_SHELL_PATH if conf.DETECTED_SHELL_PATH else 'bash'
101-
stdout = run_cmd([bash_interpreter, '-c', 'echo ${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}'], env=env)
102-
bash_maj_min = float(stdout)
103-
return bash_maj_min
104-
105-
@status_message('Shell completion', die_on_err=False)
96+
@status_message('Shell completion', msg_result='Autocompletion code generated')
10697
def autocompletion(self) -> str:
107-
bash_maj_min = self.get_bash_major_minor()
108-
# Click supports bash version >= 4.4
109-
# https://click.palletsprojects.com/en/8.1.x/changes/#version-8-0-0
110-
if bash_maj_min < 4.4:
111-
raise RuntimeError('Autocompletion not supported')
112-
113-
env = self.expanded_env()
114-
env['LANG'] = 'en'
115-
env['_IDF.PY_COMPLETE'] = 'bash_source' if self.click_ver() >= 8 else 'source_bash'
116-
stdout: str = run_cmd([sys.executable, conf.IDF_PY], env=env)
117-
return stdout
98+
bash_source = 'bash_source' if self.click_ver() >= 8 else 'source_bash'
99+
autocom = textwrap.dedent(f"""
100+
WARNING_MSG="WARNING: Failed to load shell autocompletion for bash version: $BASH_VERSION!"
101+
if test ${{BASH_VERSINFO[0]}} -lt 4
102+
then
103+
echo "$WARNING_MSG"
104+
else
105+
if ! eval "$(env LANG=en _IDF.PY_COMPLETE={bash_source} idf.py)"
106+
then
107+
echo "$WARNING_MSG"
108+
fi
109+
fi
110+
""")
111+
112+
return autocom
118113

119114
def init_file(self) -> None:
120115
with open(self.script_file_path, 'w') as fd:
@@ -133,13 +128,19 @@ def spawn(self) -> None:
133128

134129

135130
class ZshShell(UnixShell):
136-
@status_message('Shell completion', die_on_err=False)
131+
@status_message('Shell completion', msg_result='Autocompletion code generated')
137132
def autocompletion(self) -> str:
138-
env = self.expanded_env()
139-
env['LANG'] = 'en'
140-
env['_IDF.PY_COMPLETE'] = 'zsh_source' if self.click_ver() >= 8 else 'source_zsh'
141-
stdout = run_cmd([sys.executable, conf.IDF_PY], env=env)
142-
return f'autoload -Uz compinit && compinit -u\n{stdout}'
133+
zsh_source = 'zsh_source' if self.click_ver() >= 8 else 'source_zsh'
134+
autocom = textwrap.dedent(f"""
135+
WARNING_MSG="WARNING: Failed to load shell autocompletion for zsh version: $ZSH_VERSION!"
136+
autoload -Uz compinit && compinit -u
137+
if ! eval "$(env _IDF.PY_COMPLETE={zsh_source} idf.py)"
138+
then
139+
echo "$WARNING_MSG"
140+
fi
141+
""")
142+
143+
return autocom
143144

144145
def init_file(self) -> None:
145146
# If ZDOTDIR is unset, HOME is used instead.
@@ -188,6 +189,10 @@ def autocompletion(self) -> str:
188189
stdout: str = run_cmd([sys.executable, conf.IDF_PY], env=env)
189190
return stdout
190191

192+
def init_file(self) -> None:
193+
with open(self.script_file_path, 'w') as fd:
194+
self.export_file(fd)
195+
191196
def spawn(self) -> None:
192197
self.init_file()
193198
new_env = os.environ.copy()

tools/export_utils/utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ def __init__(self) -> None:
2222
self.IDF_TOOLS_PY = os.path.join(self.IDF_PATH, 'tools', 'idf_tools.py')
2323
self.IDF_PY = os.path.join(self.IDF_PATH, 'tools', 'idf.py')
2424
self.ARGS: Optional[argparse.Namespace] = None
25-
self.DETECTED_SHELL_PATH: str = ''
2625

2726

2827
# Global variable instance

0 commit comments

Comments
 (0)