Merge branch 'fix/idf_tools_install_tool_version_v5.2' into 'release/v5.2'

fix(tools): fixed command `idf_tools.py install tool@version` (v5.2)

See merge request espressif/esp-idf!40043
This commit is contained in:
Roland Dobai
2025-06-20 17:11:55 +02:00
3 changed files with 65 additions and 18 deletions

View File

@@ -125,7 +125,9 @@ test_idf_tools:
entrypoint: [""] # use system python3. no extra pip package installed entrypoint: [""] # use system python3. no extra pip package installed
script: script:
# Tools must be downloaded for testing # Tools must be downloaded for testing
- python3 ${IDF_PATH}/tools/idf_tools.py download required qemu-riscv32 qemu-xtensa # We could use "idf_tools.py download all", but we don't want to install clang because of its huge size
# cmake@version that is supported
- python3 ${IDF_PATH}/tools/idf_tools.py download required qemu-riscv32 qemu-xtensa cmake cmake@3.16.3
- cd ${IDF_PATH}/tools/test_idf_tools - cd ${IDF_PATH}/tools/test_idf_tools
- python3 -m pip install jsonschema - python3 -m pip install jsonschema
- python3 ./test_idf_tools.py -v - python3 ./test_idf_tools.py -v

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# coding=utf-8 # coding=utf-8
# #
# SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@@ -793,13 +793,36 @@ class IDFTool(object):
return recommended_versions[0] return recommended_versions[0]
return None return None
def get_preferred_installed_version(self): # type: () -> Optional[str] def get_preferred_installed_version(self) -> Optional[str]:
recommended_versions = [k for k in self.versions_installed """
if self.versions[k].status == IDFToolVersion.STATUS_RECOMMENDED Get the preferred installed version of the tool.
and self.versions[k].compatible_with_platform(self._platform)] If more versions installed, return recommended version if exists, otherwise return the highest supported version
assert len(recommended_versions) <= 1 """
if recommended_versions:
return recommended_versions[0] self.find_installed_versions()
if self.get_recommended_version() in self.versions_installed:
return self.get_recommended_version()
supported_installed_versions = [
k
for k in self.versions_installed
if self.versions[k].status == IDFToolVersion.STATUS_SUPPORTED
and self.versions[k].compatible_with_platform(self._platform)
]
sorted_supported_installed_versions = sorted(
supported_installed_versions, key=lambda x: self.versions[x], reverse=True
)
if sorted_supported_installed_versions:
warn(
''.join(
[
f'Using supported version {sorted_supported_installed_versions[0]} for tool {self.name} ',
f'as recommended version {self.get_recommended_version()} is not installed.',
]
)
)
return sorted_supported_installed_versions[0]
return None return None
def find_installed_versions(self): # type: () -> None def find_installed_versions(self): # type: () -> None
@@ -845,7 +868,6 @@ class IDFTool(object):
if ver_str != version: if ver_str != version:
warn('tool {} version {} is installed, but has reported version {}'.format( warn('tool {} version {} is installed, but has reported version {}'.format(
self.name, version, ver_str)) self.name, version, ver_str))
else:
self.versions_installed.append(version) self.versions_installed.append(version)
def latest_installed_version(self): # type: () -> Optional[str] def latest_installed_version(self): # type: () -> Optional[str]
@@ -1502,6 +1524,9 @@ def expand_tools_arg(tools_spec, overall_tools, targets): # type: (list[str], O
# Filtering by ESP_targets # Filtering by ESP_targets
tools = [k for k in tools if overall_tools[k].is_supported_for_any_of_targets(targets)] tools = [k for k in tools if overall_tools[k].is_supported_for_any_of_targets(targets)]
# Processing specific version of tool - defined with '@'
tools.extend([tool_pattern for tool_pattern in tools_spec if '@' in tool_pattern])
return tools return tools
@@ -1795,7 +1820,6 @@ def process_tool(
tool_export_paths: List[str] = [] tool_export_paths: List[str] = []
tool_export_vars: Dict[str, str] = {} tool_export_vars: Dict[str, str] = {}
tool.find_installed_versions()
recommended_version_to_use = tool.get_preferred_installed_version() recommended_version_to_use = tool.get_preferred_installed_version()
if not tool.is_executable and recommended_version_to_use: if not tool.is_executable and recommended_version_to_use:
@@ -2531,8 +2555,10 @@ def action_uninstall(args): # type: (Any) -> None
for tool in installed_tools: for tool in installed_tools:
tool_versions = os.listdir(os.path.join(tools_path, tool)) if os.path.isdir(os.path.join(tools_path, tool)) else [] tool_versions = os.listdir(os.path.join(tools_path, tool)) if os.path.isdir(os.path.join(tools_path, tool)) else []
try: try:
unused_versions = ([x for x in tool_versions if x != tools_info[tool].get_recommended_version()]) unused_versions = [x for x in tool_versions if x != tools_info[tool].get_preferred_installed_version()]
except KeyError: # When tool that is not supported by tools_info (tools.json) anymore, remove the whole tool file except (
KeyError
): # When tool that is not supported by tools_info (tools.json) anymore, remove the whole tool file
unused_versions = [''] unused_versions = ['']
if unused_versions: if unused_versions:
unused_tools_versions[tool] = unused_versions unused_tools_versions[tool] = unused_versions
@@ -2577,7 +2603,7 @@ def action_uninstall(args): # type: (Any) -> None
tool_name, tool_version = tool_spec.split('@', 1) tool_name, tool_version = tool_spec.split('@', 1)
tool_obj = tools_info_for_platform[tool_name] tool_obj = tools_info_for_platform[tool_name]
if tool_version is None: if tool_version is None:
tool_version = tool_obj.get_recommended_version() tool_version = tool_obj.get_preferred_installed_version()
# mypy-checks # mypy-checks
if tool_version is not None: if tool_version is not None:
archive_version = tool_obj.versions[tool_version].get_download_for_platform(CURRENT_PLATFORM) archive_version = tool_obj.versions[tool_version].get_download_for_platform(CURRENT_PLATFORM)

View File

@@ -502,10 +502,29 @@ class TestUsage(unittest.TestCase):
def test_export_supported_version_cmake(self): def test_export_supported_version_cmake(self):
tool_to_test = 'cmake' tool_to_test = 'cmake'
self.run_idf_tools_with_action(['install']) supported_version = ''
output = self.run_idf_tools_with_action(['export']) recommended_version = ''
for tool in self.tools_dict['tools']:
if tool['name'] != tool_to_test:
continue
for version in tool['versions']:
if version['status'] == 'supported':
supported_version = version['name']
elif version['status'] == 'recommended':
recommended_version = version['name']
self.assertNotIn(tool_to_test, output) self.run_idf_tools_with_action(['install'])
output = self.run_idf_tools_with_action(['install', f'{tool_to_test}@{supported_version}'])
self.assert_tool_installed(output, tool_to_test, supported_version)
# Remove the recommended version folder installed by install command (in case of Windows)
recommended_version_folder = os.path.join(self.temp_tools_dir, 'tools', tool_to_test, recommended_version)
if os.path.exists(recommended_version_folder):
shutil.rmtree(recommended_version_folder)
output = self.run_idf_tools_with_action(['export'])
self.assertIn(os.path.join(tool_to_test, supported_version), output)
self.assertNotIn(os.path.join(tool_to_test, recommended_version), output)
class TestMaintainer(unittest.TestCase): class TestMaintainer(unittest.TestCase):