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.exec import package_exec_cmd
|
||||||
from platformio.package.commands.install import package_install_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.outdated import package_outdated_cmd
|
||||||
from platformio.package.commands.pack import package_pack_cmd
|
from platformio.package.commands.pack import package_pack_cmd
|
||||||
from platformio.package.commands.publish import package_publish_cmd
|
from platformio.package.commands.publish import package_publish_cmd
|
||||||
@ -29,6 +30,7 @@ from platformio.package.commands.update import package_update_cmd
|
|||||||
commands=[
|
commands=[
|
||||||
package_exec_cmd,
|
package_exec_cmd,
|
||||||
package_install_cmd,
|
package_install_cmd,
|
||||||
|
package_list_cmd,
|
||||||
package_outdated_cmd,
|
package_outdated_cmd,
|
||||||
package_pack_cmd,
|
package_pack_cmd,
|
||||||
package_publish_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