Merge branch 'feature/remove_unused_tools' into 'master'

tools: Remove tools that are not used by active ESP-IDF version.

Closes IDF-2054

See merge request espressif/esp-idf!16586
This commit is contained in:
Roland Dobai
2022-02-24 06:29:34 +00:00
7 changed files with 192 additions and 35 deletions

View File

@@ -977,6 +977,16 @@ def dump_tools_json(tools_info): # type: ignore
return json.dumps(file_json, indent=2, separators=(',', ': '), sort_keys=True)
def get_python_exe_and_subdir() -> Tuple[str, str]:
if sys.platform == 'win32':
subdir = 'Scripts'
python_exe = 'python.exe'
else:
subdir = 'bin'
python_exe = 'python'
return python_exe, subdir
def get_python_env_path(): # type: () -> Tuple[str, str, str, str]
python_ver_major_minor = '{}.{}'.format(sys.version_info.major, sys.version_info.minor)
@@ -1021,13 +1031,7 @@ def get_python_env_path(): # type: () -> Tuple[str, str, str, str]
idf_python_env_path = os.path.join(global_idf_tools_path, 'python_env', # type: ignore
'idf{}_py{}_env'.format(idf_version, python_ver_major_minor))
if sys.platform == 'win32':
subdir = 'Scripts'
python_exe = 'python.exe'
else:
subdir = 'bin'
python_exe = 'python'
python_exe, subdir = get_python_exe_and_subdir()
idf_python_export_path = os.path.join(idf_python_env_path, subdir)
virtualenv_python = os.path.join(idf_python_export_path, python_exe)
@@ -1377,39 +1381,53 @@ def apply_github_assets_option(tool_download_obj): # type: ignore
tool_download_obj.url = new_url
def get_tools_spec_and_platform_info(selected_platform, targets, tools_spec,
quiet=False): # type: (str, list[str], list[str], bool) -> Tuple[list[str], Dict[str, IDFTool]]
if selected_platform not in PLATFORM_FROM_NAME:
fatal(f'unknown platform: {selected_platform}')
raise SystemExit(1)
selected_platform = PLATFORM_FROM_NAME[selected_platform]
# If this function is not called from action_download, but is used just for detecting active tools, info about downloading is unwanted.
global global_quiet
try:
old_global_quiet = global_quiet
global_quiet = quiet
tools_info = load_tools_info()
tools_info_for_platform = OrderedDict()
for name, tool_obj in tools_info.items():
tool_for_platform = tool_obj.copy_for_platform(selected_platform)
tools_info_for_platform[name] = tool_for_platform
if not tools_spec or 'required' in tools_spec:
# Downloading tools for all ESP_targets required by the operating system.
tools_spec = [k for k, v in tools_info_for_platform.items() if v.get_install_type() == IDFTool.INSTALL_ALWAYS]
# Filtering tools user defined list of ESP_targets
if 'all' not in targets:
def is_tool_selected(tool): # type: (IDFTool) -> bool
supported_targets = tool.get_supported_targets()
return (any(item in targets for item in supported_targets) or supported_targets == ['all'])
tools_spec = [k for k in tools_spec if is_tool_selected(tools_info[k])]
info('Downloading tools for {}: {}'.format(selected_platform, ', '.join(tools_spec)))
# Downloading tools for all ESP_targets (MacOS, Windows, Linux)
elif 'all' in tools_spec:
tools_spec = [k for k, v in tools_info_for_platform.items() if v.get_install_type() != IDFTool.INSTALL_NEVER]
info('Downloading tools for {}: {}'.format(selected_platform, ', '.join(tools_spec)))
finally:
global_quiet = old_global_quiet
return tools_spec, tools_info_for_platform
def action_download(args): # type: ignore
tools_info = load_tools_info()
tools_spec = args.tools
targets = [] # type: list[str]
# Installing only single tools, no targets are specified.
if 'required' in tools_spec:
targets = add_and_save_targets(args.targets)
if args.platform not in PLATFORM_FROM_NAME:
fatal('unknown platform: {}' % args.platform)
raise SystemExit(1)
platform = PLATFORM_FROM_NAME[args.platform]
tools_info_for_platform = OrderedDict()
for name, tool_obj in tools_info.items():
tool_for_platform = tool_obj.copy_for_platform(platform)
tools_info_for_platform[name] = tool_for_platform
if not tools_spec or 'required' in tools_spec:
# Downloading tools for all ESP_targets required by the operating system.
tools_spec = [k for k, v in tools_info_for_platform.items() if v.get_install_type() == IDFTool.INSTALL_ALWAYS]
# Filtering tools user defined list of ESP_targets
if 'all' not in targets:
def is_tool_selected(tool): # type: (IDFTool) -> bool
supported_targets = tool.get_supported_targets()
return (any(item in targets for item in supported_targets) or supported_targets == ['all'])
tools_spec = [k for k in tools_spec if is_tool_selected(tools_info[k])]
info('Downloading tools for {}: {}'.format(platform, ', '.join(tools_spec)))
# Downloading tools for all ESP_targets (MacOS, Windows, Linux)
elif 'all' in tools_spec:
tools_spec = [k for k, v in tools_info_for_platform.items() if v.get_install_type() != IDFTool.INSTALL_NEVER]
info('Downloading tools for {}: {}'.format(platform, ', '.join(tools_spec)))
tools_spec, tools_info_for_platform = get_tools_spec_and_platform_info(args.platform, targets, args.tools)
for tool_spec in tools_spec:
if '@' not in tool_spec:
@@ -1750,6 +1768,75 @@ def action_rewrite(args): # type: ignore
info('Wrote output to {}'.format(args.output))
def action_uninstall(args): # type: (Any) -> None
""" Print or remove installed tools, that are currently not used by active ESP-IDF version.
Additionally remove all older versions of previously downloaded archives.
"""
def is_tool_selected(tool): # type: (IDFTool) -> bool
supported_targets = tool.get_supported_targets()
return (supported_targets == ['all'] or any(item in targets for item in supported_targets))
tools_info = load_tools_info()
targets, _ = get_requested_targets_and_features()
tools_path = os.path.join(global_idf_tools_path or '', 'tools')
dist_path = os.path.join(global_idf_tools_path or '', 'dist')
used_tools = [k for k, v in tools_info.items() if (v.get_install_type() == IDFTool.INSTALL_ALWAYS and is_tool_selected(tools_info[k]))]
installed_tools = os.listdir(tools_path) if os.path.isdir(tools_path) else []
unused_tools = [tool for tool in installed_tools if tool not in used_tools]
# Keeping tools added by windows installer
KEEP_WIN_TOOLS = ['idf-git', 'idf-python']
for tool in KEEP_WIN_TOOLS:
if tool in unused_tools:
unused_tools.remove(tool)
# Print unused tools.
if args.dry_run:
if unused_tools:
print('For removing {} use command \'{} {} {}\''.format(', '.join(unused_tools), get_python_exe_and_subdir()[0],
os.path.join(global_idf_path or '', 'tools', 'idf_tools.py'), 'uninstall'))
return
# Remove installed tools that are not used by current ESP-IDF version.
for tool in unused_tools:
try:
shutil.rmtree(os.path.join(tools_path, tool))
info(os.path.join(tools_path, tool) + ' was removed.')
except OSError as error:
warn(f'{error.filename} can not be removed because {error.strerror}.')
# Remove old archives versions and archives that are not used by the current ESP-IDF version.
if args.remove_archives:
targets, _ = get_requested_targets_and_features()
tools_spec, tools_info_for_platform = get_tools_spec_and_platform_info(CURRENT_PLATFORM, targets, ['required'], quiet=True)
used_archives = []
# Detect used active archives
for tool_spec in tools_spec:
if '@' not in tool_spec:
tool_name = tool_spec
tool_version = None
else:
tool_name, tool_version = tool_spec.split('@', 1)
tool_obj = tools_info_for_platform[tool_name]
if tool_version is None:
tool_version = tool_obj.get_recommended_version()
# mypy-checks
if tool_version is not None:
archive_version = tool_obj.versions[tool_version].get_download_for_platform(CURRENT_PLATFORM)
if archive_version is not None:
archive_version_url = archive_version.url
archive = os.path.basename(archive_version_url)
used_archives.append(archive)
downloaded_archives = os.listdir(dist_path)
for archive in downloaded_archives:
if archive not in used_archives:
os.remove(os.path.join(dist_path, archive))
info(os.path.join(dist_path, archive) + ' was removed.')
def action_validate(args): # type: ignore
try:
import jsonschema
@@ -1885,6 +1972,10 @@ def main(argv): # type: (list[str]) -> None
download.add_argument('--targets', default='all', help='A comma separated list of desired chip targets for installing.' +
' It defaults to installing all supported targets.')
uninstall = subparsers.add_parser('uninstall', help='Remove installed tools, that are not used by current version of ESP-IDF.')
uninstall.add_argument('--dry-run', help='Print unused tools.', action='store_true')
uninstall.add_argument('--remove-archives', help='Remove old archive versions and archives from unused tools.', action='store_true')
if IDF_MAINTAINER:
for subparser in [download, install]:
subparser.add_argument('--mirror-prefix-map', nargs='*',