Merge branch 'feature/list_outdated_tools' into 'master'

tools: add new outdated sub-command for idf_tools.py

Closes IDF-6417

See merge request espressif/esp-idf!22241
This commit is contained in:
Roland Dobai
2023-02-10 16:58:13 +08:00
3 changed files with 86 additions and 2 deletions

View File

@@ -107,6 +107,10 @@ Any mirror server can be used provided the URL matches the ``github.com`` downlo
* ``list``: Lists the known versions of the tools, and indicates which ones are installed.
Following options are available to customize the output.
- ``--outdated``: List only outdated versions of tools installed in ``IDF_TOOLS_PATH``.
* ``check``: For each tool, checks whether the tool is available in the system path and in ``IDF_TOOLS_PATH``.
* ``install-python-env``: Create a Python virtual environment in the ``${IDF_TOOLS_PATH}/python_env`` directory and install there the required Python packages. An optional ``--features`` argument allows one to specify a comma-separated list of features to be added or removed. Feature that begins with ``-`` will be removed and features with ``+`` or without any sign will be added. Example syntax for removing feature ``XY`` is ``--features=-XY`` and for adding ``--features=+XY`` or ``--features=XY``. If both removing and adding options are provided with the same feature, no operation is performed. For each feature a requirements file must exist. For example, feature ``XY`` is a valid feature if ``${IDF_PATH}/tools/requirements/requirements.XY.txt`` is an existing file with a list of Python packages to be installed. There is one mandatory ``core`` feature ensuring core functionality of ESP-IDF (build, flash, monitor, debug in console). There can be an arbitrary number of optional features. The selected list of features is stored in ``idf-env.json``. The requirement files contain a list of the desired Python packages to be installed and ``espidf.constraints.*.txt`` downloaded from https://dl.espressif.com and stored in ``${IDF_TOOLS_PATH}`` the package version requirements for a given ESP-IDF version. Althought it is not recommended, the download and use of constraint files can be disabled with the ``--no-constraints`` argument or setting the ``IDF_PYTHON_CHECK_CONSTRAINTS`` environment variable to ``no``.

View File

@@ -759,6 +759,31 @@ class IDFTool(object):
else:
self.versions_installed.append(version)
def latest_installed_version(self): # type: () -> Optional[str]
"""
Get the latest installed tool version by directly checking the
tool's version directories.
"""
tool_path = self.get_path()
if not os.path.exists(tool_path):
return None
dentries = os.listdir(tool_path)
dirs = [d for d in dentries if os.path.isdir(os.path.join(tool_path, d))]
for version in sorted(dirs, reverse=True):
# get_path_for_version() has assert to check if version is in versions
# dict, so get_export_paths() cannot be used. Let's just create the
# export paths list directly here.
paths = [os.path.join(tool_path, version, *p) for p in self._current_options.export_paths]
try:
ver_str = self.get_version(paths)
except (ToolNotFound, ToolExecError):
continue
if ver_str != version:
continue
return version
return None
def download(self, version): # type: (str) -> None
assert version in self.versions
download_obj = self.versions[version].get_download_for_platform(self._platform)
@@ -1495,7 +1520,7 @@ def active_repo_id() -> str:
return global_idf_path + '-v' + get_idf_version()
def action_list(args): # type: ignore
def list_default(args): # type: ignore
tools_info = load_tools_info()
for name, tool in tools_info.items():
if tool.get_install_type() == IDFTool.INSTALL_NEVER:
@@ -1514,6 +1539,29 @@ def action_list(args): # type: ignore
', installed' if version in tool.versions_installed else ''))
def list_outdated(args): # type: ignore
tools_info = load_tools_info()
for name, tool in tools_info.items():
if tool.get_install_type() == IDFTool.INSTALL_NEVER:
continue
versions_for_platform = {k: v for k, v in tool.versions.items() if v.compatible_with_platform()}
if not versions_for_platform:
continue
version_installed = tool.latest_installed_version()
if not version_installed:
continue
version_available = sorted(versions_for_platform.keys(), key=tool.versions.get, reverse=True)[0]
if version_installed < version_available:
info(f'{name}: version {version_installed} is outdated by {version_available}')
def action_list(args): # type: ignore
if args.outdated:
list_outdated(args)
else:
list_default(args)
def action_check(args): # type: ignore
tools_info = load_tools_info()
tools_info = filter_tools_info(IDFEnv.get_idf_env(), tools_info)
@@ -2447,7 +2495,8 @@ def main(argv): # type: (list[str]) -> None
parser.add_argument('--idf-path', help='ESP-IDF path to use')
subparsers = parser.add_subparsers(dest='action')
subparsers.add_parser('list', help='List tools and versions available')
list_parser = subparsers.add_parser('list', help='List tools and versions available')
list_parser.add_argument('--outdated', help='Print only outdated installed tools', action='store_true')
subparsers.add_parser('check', help='Print summary of tools installed or found in PATH')
export = subparsers.add_parser('export', help='Output command for setting tool paths, suitable for shell')
export.add_argument('--format', choices=[EXPORT_SHELL, EXPORT_KEY_VALUE], default=EXPORT_SHELL,

View File

@@ -190,6 +190,37 @@ class TestUsage(unittest.TestCase):
self.assertIn('%s/tools/esp-rom-elfs/%s/' %
(self.temp_tools_dir, ESP_ROM_ELFS_VERSION), output)
output = self.run_idf_tools_with_action(['list', '--outdated'])
self.assertEqual('', output)
tools_json_outdated = os.path.join(self.temp_tools_dir, 'tools', 'tools.outdated.json')
new_version = 'zzzzzz'
self.run_idf_tools_with_action(
[
'add-version',
'--tool',
XTENSA_ESP32_ELF,
'--url-prefix',
'http://test.com',
'--version',
new_version,
'--override',
'--checksum-file',
'add_version/checksum.sha256',
'--output',
tools_json_outdated
])
output = self.run_idf_tools_with_action(
[
'--tools-json',
tools_json_outdated,
'list',
'--outdated'
])
self.assertIn((f'{XTENSA_ESP32_ELF}: version {XTENSA_ESP32_ELF_VERSION} '
f'is outdated by {new_version}'), output)
def test_tools_for_esp32(self):
required_tools_installed = 5
output = self.run_idf_tools_with_action(['install', '--targets=esp32'])