From 71f9401e23e775026837d541377729be7683a047 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Sat, 2 Apr 2022 22:30:35 +0300 Subject: [PATCH] Fixed an issue when manually removed dependencies were not uninstalled from the storage // Resolve #3076 --- HISTORY.rst | 7 ++-- platformio/package/commands/install.py | 30 +++++++++++++++- platformio/package/commands/list.py | 4 ++- tests/commands/pkg/test_install.py | 50 ++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index c371d8c7..84046623 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -30,6 +30,7 @@ PlatformIO Core 5 - Ignore files according to the patterns declared in ".gitignore" when using the `pio package pack `__ command (`issue #4188 `_) - Dropped automatic updates of global libraries and development platforms (`issue #4179 `_) - Dropped support for the "pythonPackages" field in "platform.json" manifest in favor of `Extra Python Dependencies `__ + - Fixed an issue when manually removed dependencies from the `"platformio.ini" `__ configuration file were not uninstalled from the storage (`issue #3076 `_) * **Static Code Analysis** @@ -38,9 +39,9 @@ PlatformIO Core 5 * **Miscellaneous** -* Show project dependency licenses when building in the verbose mode -* Improved PIO Remote setup on credit-card sized computers (Raspberry Pi, BeagleBon, etc) (`issue #3865 `_) -* Better handling of the failed tests using the `Unit Testing `__ solution. + - Show project dependency licenses when building in the verbose mode + - Improved PIO Remote setup on credit-card sized computers (Raspberry Pi, BeagleBon, etc) (`issue #3865 `_) + - Better handling of the failed tests using the `Unit Testing `__ solution. 5.2.5 (2022-02-10) ~~~~~~~~~~~~~~~~~~ diff --git a/platformio/package/commands/install.py b/platformio/package/commands/install.py index 48bbcd95..ec2c86cd 100644 --- a/platformio/package/commands/install.py +++ b/platformio/package/commands/install.py @@ -14,10 +14,12 @@ import logging import os +from pathlib import Path import click from platformio import fs +from platformio.package.exception import UnknownPackageError from platformio.package.manager.library import LibraryPackageManager from platformio.package.manager.platform import PlatformPackageManager from platformio.package.manager.tool import ToolPackageManager @@ -197,6 +199,7 @@ def _install_project_env_custom_tools(project_env, options): def _install_project_env_libraries(project_env, options): + _uninstall_project_unused_libdeps(project_env, options) already_up_to_date = not options.get("force") config = ProjectConfig.get_instance() env_lm = LibraryPackageManager( @@ -220,12 +223,37 @@ def _install_project_env_libraries(project_env, options): skip_dependencies=options.get("skip_dependencies"), force=options.get("force"), ) - # install dependencies from the priate libraries + # install dependencies from the private libraries for pkg in private_lm.get_installed(): _install_project_private_library_deps(pkg, private_lm, env_lm, options) return not already_up_to_date +def _uninstall_project_unused_libdeps(project_env, options): + config = ProjectConfig.get_instance() + lib_deps = set(config.get(f"env:{project_env}", "lib_deps")) + if not lib_deps: + return + storage_dir = Path(config.get("platformio", "libdeps_dir"), project_env) + integrity_dat = storage_dir / "integrity.dat" + if integrity_dat.is_file(): + prev_lib_deps = set(integrity_dat.read_text().strip().split("\n")) + if lib_deps == prev_lib_deps: + return + lm = LibraryPackageManager(str(storage_dir)) + if options.get("silent"): + lm.set_log_level(logging.WARN) + else: + click.secho("Removing unused dependencies...") + for spec in set(prev_lib_deps) - set(lib_deps): + try: + lm.uninstall(spec) + except UnknownPackageError: + pass + storage_dir.mkdir(parents=True, exist_ok=True) + integrity_dat.write_text("\n".join(lib_deps), encoding="utf-8") + + def _install_project_private_library_deps(private_pkg, private_lm, env_lm, options): for dependency in private_lm.get_pkg_dependencies(private_pkg) or []: spec = private_lm.dependency_to_spec(dependency) diff --git a/platformio/package/commands/list.py b/platformio/package/commands/list.py index e54c3924..578ecd20 100644 --- a/platformio/package/commands/list.py +++ b/platformio/package/commands/list.py @@ -58,7 +58,9 @@ def package_list_cmd(**options): def humanize_package(pkg, spec=None, verbose=False): if spec and not isinstance(spec, PackageSpec): spec = PackageSpec(spec) - data = [click.style("{name}@{version}".format(**pkg.metadata.as_dict()), fg="cyan")] + data = [ + click.style("{name} @ {version}".format(**pkg.metadata.as_dict()), fg="cyan") + ] extra_data = ["required: %s" % (spec.humanize() if spec else "Any")] if verbose: extra_data.append(pkg.path) diff --git a/tests/commands/pkg/test_install.py b/tests/commands/pkg/test_install.py index d99e8f0d..f15dab33 100644 --- a/tests/commands/pkg/test_install.py +++ b/tests/commands/pkg/test_install.py @@ -253,6 +253,56 @@ platform = native ] +def test_remove_project_unused_libdeps( + clirunner, validate_cliresult, isolated_pio_core, tmp_path +): + project_dir = tmp_path / "project" + project_dir.mkdir() + (project_dir / "platformio.ini").write_text(PROJECT_CONFIG_TPL) + result = clirunner.invoke( + package_install_cmd, + ["-d", str(project_dir), "-e", "baremetal"], + ) + validate_cliresult(result) + with fs.cd(str(project_dir)): + config = ProjectConfig() + storage_dir = os.path.join(config.get("platformio", "libdeps_dir"), "baremetal") + lm = LibraryPackageManager(storage_dir) + assert pkgs_to_specs(lm.get_installed()) == [ + PackageSpec("DallasTemperature@3.9.1"), + PackageSpec("OneWire@2.3.6"), + ] + + # add new deps + lib_deps = config.get("env:baremetal", "lib_deps") + config.set( + "env:baremetal", "lib_deps", lib_deps + ["bblanchon/ArduinoJson@^6.19.2"] + ) + config.save() + result = clirunner.invoke( + package_install_cmd, + ["-e", "baremetal"], + ) + validate_cliresult(result) + lm = LibraryPackageManager(storage_dir) + assert pkgs_to_specs(lm.get_installed()) == [ + PackageSpec("ArduinoJson@6.19.3"), + PackageSpec("DallasTemperature@3.9.1"), + PackageSpec("OneWire@2.3.6"), + ] + + # manually remove from cofiguration file + config.set("env:baremetal", "lib_deps", ["bblanchon/ArduinoJson@^6.19.2"]) + config.save() + result = clirunner.invoke( + package_install_cmd, + ["-e", "baremetal"], + ) + validate_cliresult(result) + lm = LibraryPackageManager(storage_dir) + assert pkgs_to_specs(lm.get_installed()) == [PackageSpec("ArduinoJson@6.19.3")] + + def test_unknown_project_dependencies( clirunner, validate_cliresult, isolated_pio_core, tmp_path ):