forked from platformio/platformio-core
Implement pio pkg list
command // Issue #3373
This commit is contained in:
2
docs
2
docs
Submodule docs updated: cbf179f826...a04cdef33c
@ -16,6 +16,7 @@ import click
|
||||
|
||||
from platformio.package.commands.exec import package_exec_cmd
|
||||
from platformio.package.commands.install import package_install_cmd
|
||||
from platformio.package.commands.list import package_list_cmd
|
||||
from platformio.package.commands.outdated import package_outdated_cmd
|
||||
from platformio.package.commands.pack import package_pack_cmd
|
||||
from platformio.package.commands.publish import package_publish_cmd
|
||||
@ -29,6 +30,7 @@ from platformio.package.commands.update import package_update_cmd
|
||||
commands=[
|
||||
package_exec_cmd,
|
||||
package_install_cmd,
|
||||
package_list_cmd,
|
||||
package_outdated_cmd,
|
||||
package_pack_cmd,
|
||||
package_publish_cmd,
|
||||
|
219
platformio/package/commands/list.py
Normal file
219
platformio/package/commands/list.py
Normal file
@ -0,0 +1,219 @@
|
||||
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
import click
|
||||
|
||||
from platformio import fs
|
||||
from platformio.package.manager.library import LibraryPackageManager
|
||||
from platformio.package.manager.platform import PlatformPackageManager
|
||||
from platformio.package.manager.tool import ToolPackageManager
|
||||
from platformio.package.meta import PackageItem, PackageSpec
|
||||
from platformio.platform.factory import PlatformFactory
|
||||
from platformio.project.config import ProjectConfig
|
||||
|
||||
|
||||
@click.command("list", short_help="List installed packages")
|
||||
@click.option(
|
||||
"-d",
|
||||
"--project-dir",
|
||||
default=os.getcwd,
|
||||
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
|
||||
)
|
||||
@click.option("-e", "--environment", "environments", multiple=True)
|
||||
@click.option("-p", "--platform", "platforms", metavar="SPECIFICATION", multiple=True)
|
||||
@click.option("-t", "--tool", "tools", metavar="SPECIFICATION", multiple=True)
|
||||
@click.option("-l", "--library", "libraries", metavar="SPECIFICATION", multiple=True)
|
||||
@click.option("-g", "--global", is_flag=True, help="List globally installed packages")
|
||||
@click.option(
|
||||
"--storage-dir",
|
||||
default=None,
|
||||
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
|
||||
help="Custom Package Manager storage for global packages",
|
||||
)
|
||||
@click.option("--only-platforms", is_flag=True, help="List only platform packages")
|
||||
@click.option("--only-tools", is_flag=True, help="List only tool packages")
|
||||
@click.option("--only-libraries", is_flag=True, help="List only library packages")
|
||||
@click.option("-v", "--verbose", is_flag=True)
|
||||
def package_list_cmd(**options):
|
||||
if options.get("global"):
|
||||
list_global_packages(options)
|
||||
else:
|
||||
list_project_packages(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")]
|
||||
extra_data = ["required: %s" % (spec.humanize() if spec else "Any")]
|
||||
if verbose:
|
||||
extra_data.append(pkg.path)
|
||||
data.append("(%s)" % ", ".join(extra_data))
|
||||
return " ".join(data)
|
||||
|
||||
|
||||
def print_dependency_tree(pm, specs=None, filter_specs=None, level=0, verbose=False):
|
||||
filtered_pkgs = [
|
||||
pm.get_package(spec) for spec in filter_specs or [] if pm.get_package(spec)
|
||||
]
|
||||
candidates = {}
|
||||
if specs:
|
||||
for spec in specs:
|
||||
pkg = pm.get_package(spec)
|
||||
if not pkg:
|
||||
continue
|
||||
candidates[pkg.path] = (pkg, spec)
|
||||
else:
|
||||
candidates = {pkg.path: (pkg, pkg.metadata.spec) for pkg in pm.get_installed()}
|
||||
if not candidates:
|
||||
return
|
||||
candidates = sorted(candidates.values(), key=lambda item: item[0].metadata.name)
|
||||
for index, (pkg, spec) in enumerate(candidates):
|
||||
if filtered_pkgs and not _pkg_tree_contains(pm, pkg, filtered_pkgs):
|
||||
continue
|
||||
dependencies = pm.get_pkg_dependencies(pkg)
|
||||
click.echo(
|
||||
"%s%s %s"
|
||||
% (
|
||||
"│ " * level,
|
||||
"├──" if index < len(candidates) - 1 else "└──",
|
||||
humanize_package(
|
||||
pkg,
|
||||
spec=spec,
|
||||
verbose=verbose,
|
||||
),
|
||||
)
|
||||
)
|
||||
if dependencies:
|
||||
print_dependency_tree(
|
||||
pm,
|
||||
specs=[pm.dependency_to_spec(item) for item in dependencies],
|
||||
filter_specs=filter_specs,
|
||||
level=level + 1,
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
|
||||
def _pkg_tree_contains(pm, root: PackageItem, children: List[PackageItem]):
|
||||
if root in children:
|
||||
return True
|
||||
for dependency in pm.get_pkg_dependencies(root) or []:
|
||||
pkg = pm.get_package(pm.dependency_to_spec(dependency))
|
||||
if pkg and _pkg_tree_contains(pm, pkg, children):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def list_global_packages(options):
|
||||
data = [
|
||||
("platforms", PlatformPackageManager(options.get("storage_dir"))),
|
||||
("tools", ToolPackageManager(options.get("storage_dir"))),
|
||||
("libraries", LibraryPackageManager(options.get("storage_dir"))),
|
||||
]
|
||||
only_packages = any(
|
||||
options.get(type_) or options.get(f"only_{type_}") for (type_, _) in data
|
||||
)
|
||||
for (type_, pm) in data:
|
||||
skip_conds = [
|
||||
only_packages
|
||||
and not options.get(type_)
|
||||
and not options.get(f"only_{type_}"),
|
||||
not pm.get_installed(),
|
||||
]
|
||||
if any(skip_conds):
|
||||
continue
|
||||
click.secho(type_.capitalize(), bold=True)
|
||||
print_dependency_tree(
|
||||
pm, filter_specs=options.get(type_), verbose=options.get("verbose")
|
||||
)
|
||||
click.echo()
|
||||
|
||||
|
||||
def list_project_packages(options):
|
||||
environments = options["environments"]
|
||||
only_packages = any(
|
||||
options.get(type_) or options.get(f"only_{type_}")
|
||||
for type_ in ("platforms", "tools", "libraries")
|
||||
)
|
||||
only_platform_packages = any(
|
||||
options.get(type_) or options.get(f"only_{type_}")
|
||||
for type_ in ("platforms", "tools")
|
||||
)
|
||||
only_library_packages = options.get("libraries") or options.get(f"only_libraries")
|
||||
|
||||
with fs.cd(options["project_dir"]):
|
||||
config = ProjectConfig.get_instance()
|
||||
config.validate(environments)
|
||||
for env in config.envs():
|
||||
if environments and env not in environments:
|
||||
continue
|
||||
click.echo(
|
||||
"Resolving %s environment packages..." % click.style(env, fg="cyan")
|
||||
)
|
||||
found = False
|
||||
if not only_packages or only_platform_packages:
|
||||
_found = print_project_env_platform_packages(env, options)
|
||||
found = found or _found
|
||||
if not only_packages or only_library_packages:
|
||||
_found = print_project_env_library_packages(env, options)
|
||||
found = found or _found
|
||||
if not found:
|
||||
click.echo("No packages")
|
||||
if (not environments and len(config.envs()) > 1) or len(environments) > 1:
|
||||
click.echo()
|
||||
|
||||
|
||||
def print_project_env_platform_packages(project_env, options):
|
||||
config = ProjectConfig.get_instance()
|
||||
platform = config.get(f"env:{project_env}", "platform")
|
||||
if not platform:
|
||||
return None
|
||||
pkg = PlatformPackageManager().get_package(platform)
|
||||
if not pkg:
|
||||
return None
|
||||
click.echo(
|
||||
"Platform %s"
|
||||
% (humanize_package(pkg, platform, verbose=options.get("verbose")))
|
||||
)
|
||||
p = PlatformFactory.new(pkg)
|
||||
if project_env:
|
||||
p.configure_project_packages(project_env)
|
||||
print_dependency_tree(
|
||||
p.pm,
|
||||
specs=[p.get_package_spec(name) for name in p.packages],
|
||||
filter_specs=options.get("tools"),
|
||||
)
|
||||
click.echo()
|
||||
return True
|
||||
|
||||
|
||||
def print_project_env_library_packages(project_env, options):
|
||||
config = ProjectConfig.get_instance()
|
||||
lib_deps = config.get(f"env:{project_env}", "lib_deps")
|
||||
lm = LibraryPackageManager(
|
||||
os.path.join(config.get("platformio", "libdeps_dir"), project_env)
|
||||
)
|
||||
if not lib_deps or not lm.get_installed():
|
||||
return None
|
||||
click.echo("Libraries")
|
||||
print_dependency_tree(
|
||||
lm,
|
||||
lib_deps,
|
||||
filter_specs=options.get("libraries"),
|
||||
verbose=options.get("verbose"),
|
||||
)
|
||||
return True
|
145
tests/commands/pkg/test_list.py
Normal file
145
tests/commands/pkg/test_list.py
Normal file
@ -0,0 +1,145 @@
|
||||
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
import os
|
||||
|
||||
from platformio import fs
|
||||
from platformio.package.commands.install import package_install_cmd
|
||||
from platformio.package.commands.list import package_list_cmd
|
||||
from platformio.package.manager.library import LibraryPackageManager
|
||||
from platformio.package.manager.platform import PlatformPackageManager
|
||||
from platformio.package.manager.tool import ToolPackageManager
|
||||
from platformio.package.meta import PackageSpec
|
||||
from platformio.project.config import ProjectConfig
|
||||
|
||||
PROJECT_CONFIG_TPL = """
|
||||
[env]
|
||||
platform = platformio/atmelavr@^3.4.0
|
||||
|
||||
[env:baremetal]
|
||||
board = uno
|
||||
|
||||
[env:devkit]
|
||||
framework = arduino
|
||||
board = attiny88
|
||||
lib_deps =
|
||||
milesburton/DallasTemperature@^3.9.1
|
||||
https://github.com/bblanchon/ArduinoJson.git#v6.19.0
|
||||
"""
|
||||
|
||||
|
||||
def test_project(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)],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
|
||||
# test all envs
|
||||
result = clirunner.invoke(
|
||||
package_list_cmd,
|
||||
["-d", str(project_dir)],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
assert all(token in result.output for token in ("baremetal", "devkit"))
|
||||
assert result.output.count("Platform atmelavr@3.4.0") == 2
|
||||
assert (
|
||||
result.output.count(
|
||||
"toolchain-atmelavr@1.70300.191015 (required: "
|
||||
"platformio/toolchain-atmelavr @ ~1.70300.0)"
|
||||
)
|
||||
== 2
|
||||
)
|
||||
assert result.output.count("Libraries") == 1
|
||||
assert (
|
||||
"ArduinoJson@6.19.0+sha.9693fd2 (required: "
|
||||
"git+https://github.com/bblanchon/ArduinoJson.git#v6.19.0)"
|
||||
) in result.output
|
||||
assert "OneWire@2" in result.output
|
||||
|
||||
# test "baremetal"
|
||||
result = clirunner.invoke(
|
||||
package_list_cmd,
|
||||
["-d", str(project_dir), "-e", "baremetal"],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
assert "Platform atmelavr@3" in result.output
|
||||
assert "Libraries" not in result.output
|
||||
|
||||
# filter by "tool" package
|
||||
result = clirunner.invoke(
|
||||
package_list_cmd,
|
||||
["-d", str(project_dir), "-t", "toolchain-atmelavr@~1.70300.0"],
|
||||
)
|
||||
assert "framework-arduino" not in result.output
|
||||
assert "Libraries" not in result.output
|
||||
|
||||
# list only libraries
|
||||
result = clirunner.invoke(
|
||||
package_list_cmd,
|
||||
["-d", str(project_dir), "--only-libraries"],
|
||||
)
|
||||
assert "Platform atmelavr" not in result.output
|
||||
|
||||
# list only libraries for baremetal
|
||||
result = clirunner.invoke(
|
||||
package_list_cmd,
|
||||
["-d", str(project_dir), "-e", "baremetal", "--only-libraries"],
|
||||
)
|
||||
assert "No packages" in result.output
|
||||
|
||||
|
||||
def test_global_packages(clirunner, validate_cliresult, isolated_pio_core, tmp_path):
|
||||
result = clirunner.invoke(package_list_cmd, ["-g"])
|
||||
validate_cliresult(result)
|
||||
assert "atmelavr@3" in result.output
|
||||
assert "framework-arduino-avr-attiny" in result.output
|
||||
|
||||
# only tools
|
||||
result = clirunner.invoke(package_list_cmd, ["-g", "--only-tools"])
|
||||
validate_cliresult(result)
|
||||
assert "toolchain-atmelavr" in result.output
|
||||
assert "Platforms" not in result.output
|
||||
|
||||
# find tool package
|
||||
result = clirunner.invoke(package_list_cmd, ["-g", "-t", "toolchain-atmelavr"])
|
||||
validate_cliresult(result)
|
||||
assert "toolchain-atmelavr" in result.output
|
||||
assert "framework-arduino-avr-attiny@" not in result.output
|
||||
|
||||
# only libraries - no packages
|
||||
result = clirunner.invoke(package_list_cmd, ["-g", "--only-libraries"])
|
||||
validate_cliresult(result)
|
||||
assert not result.output.strip()
|
||||
|
||||
# check global libs
|
||||
result = clirunner.invoke(
|
||||
package_install_cmd, ["-g", "-l", "milesburton/DallasTemperature@^3.9.1"]
|
||||
)
|
||||
validate_cliresult(result)
|
||||
result = clirunner.invoke(package_list_cmd, ["-g", "--only-libraries"])
|
||||
validate_cliresult(result)
|
||||
assert "DallasTemperature" in result.output
|
||||
assert "OneWire" in result.output
|
||||
|
||||
# filter by lib
|
||||
result = clirunner.invoke(package_list_cmd, ["-g", "-l", "OneWire"])
|
||||
validate_cliresult(result)
|
||||
assert "DallasTemperature" in result.output
|
||||
assert "OneWire" in result.output
|
Reference in New Issue
Block a user