2022-03-08 14:58:01 +02:00
|
|
|
# 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 logging
|
|
|
|
import os
|
2022-04-02 22:30:35 +03:00
|
|
|
from pathlib import Path
|
2022-03-08 14:58:01 +02:00
|
|
|
|
|
|
|
import click
|
|
|
|
|
|
|
|
from platformio import fs
|
2022-04-02 22:30:35 +03:00
|
|
|
from platformio.package.exception import UnknownPackageError
|
2022-03-08 14:58:01 +02:00
|
|
|
from platformio.package.manager.library import LibraryPackageManager
|
|
|
|
from platformio.package.manager.platform import PlatformPackageManager
|
|
|
|
from platformio.package.manager.tool import ToolPackageManager
|
2022-03-09 19:07:11 +02:00
|
|
|
from platformio.package.meta import PackageSpec
|
2022-03-08 14:58:01 +02:00
|
|
|
from platformio.project.config import ProjectConfig
|
2022-03-16 16:18:59 +02:00
|
|
|
from platformio.project.savedeps import pkg_to_save_spec, save_project_dependencies
|
2022-04-26 15:09:51 +03:00
|
|
|
from platformio.test.result import TestSuite
|
|
|
|
from platformio.test.runners.factory import TestRunnerFactory
|
2022-03-08 14:58:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
@click.command(
|
|
|
|
"install", short_help="Install the project dependencies or custom 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)
|
2022-03-23 17:57:18 +02:00
|
|
|
@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)
|
2022-03-09 19:07:11 +02:00
|
|
|
@click.option(
|
|
|
|
"--no-save",
|
|
|
|
is_flag=True,
|
|
|
|
help="Prevent saving specified packages to `platformio.ini`",
|
|
|
|
)
|
2022-03-08 14:58:01 +02:00
|
|
|
@click.option("--skip-dependencies", is_flag=True, help="Skip package dependencies")
|
|
|
|
@click.option("-g", "--global", is_flag=True, help="Install package globally")
|
|
|
|
@click.option(
|
|
|
|
"--storage-dir",
|
|
|
|
default=None,
|
|
|
|
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
|
2022-03-09 19:07:11 +02:00
|
|
|
help="Custom Package Manager storage for global packages",
|
2022-03-08 14:58:01 +02:00
|
|
|
)
|
|
|
|
@click.option("-f", "--force", is_flag=True, help="Reinstall package if it exists")
|
|
|
|
@click.option("-s", "--silent", is_flag=True, help="Suppress progress reporting")
|
|
|
|
def package_install_cmd(**options):
|
|
|
|
if options.get("global"):
|
|
|
|
install_global_dependencies(options)
|
|
|
|
else:
|
|
|
|
install_project_dependencies(options)
|
|
|
|
|
|
|
|
|
|
|
|
def install_global_dependencies(options):
|
|
|
|
pm = PlatformPackageManager(options.get("storage_dir"))
|
|
|
|
tm = ToolPackageManager(options.get("storage_dir"))
|
|
|
|
lm = LibraryPackageManager(options.get("storage_dir"))
|
|
|
|
for obj in (pm, tm, lm):
|
|
|
|
obj.set_log_level(logging.WARN if options.get("silent") else logging.DEBUG)
|
|
|
|
for spec in options.get("platforms"):
|
|
|
|
pm.install(
|
|
|
|
spec,
|
2022-03-19 18:12:36 +02:00
|
|
|
skip_dependencies=options.get("skip_dependencies"),
|
2022-03-08 14:58:01 +02:00
|
|
|
force=options.get("force"),
|
|
|
|
)
|
|
|
|
for spec in options.get("tools"):
|
|
|
|
tm.install(
|
|
|
|
spec,
|
|
|
|
skip_dependencies=options.get("skip_dependencies"),
|
|
|
|
force=options.get("force"),
|
|
|
|
)
|
|
|
|
for spec in options.get("libraries", []):
|
|
|
|
lm.install(
|
|
|
|
spec,
|
|
|
|
skip_dependencies=options.get("skip_dependencies"),
|
|
|
|
force=options.get("force"),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def install_project_dependencies(options):
|
|
|
|
environments = options["environments"]
|
|
|
|
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
|
|
|
|
if not options["silent"]:
|
|
|
|
click.echo(
|
|
|
|
"Resolving %s environment packages..." % click.style(env, fg="cyan")
|
|
|
|
)
|
2022-03-16 16:18:59 +02:00
|
|
|
already_up_to_date = not install_project_env_dependencies(env, options)
|
2022-03-08 14:58:01 +02:00
|
|
|
if not options["silent"] and already_up_to_date:
|
|
|
|
click.secho("Already up-to-date.", fg="green")
|
|
|
|
|
|
|
|
|
|
|
|
def install_project_env_dependencies(project_env, options=None):
|
|
|
|
"""Used in `pio run` -> Processor"""
|
|
|
|
options = options or {}
|
2022-03-14 12:18:05 +02:00
|
|
|
installed_conds = []
|
|
|
|
# custom platforms
|
|
|
|
if options.get("platforms"):
|
|
|
|
installed_conds.append(
|
|
|
|
_install_project_env_custom_platforms(project_env, options)
|
|
|
|
)
|
|
|
|
# custom tools
|
|
|
|
if options.get("tools"):
|
2022-03-14 13:37:47 +02:00
|
|
|
installed_conds.append(_install_project_env_custom_tools(project_env, options))
|
2022-03-14 12:18:05 +02:00
|
|
|
# custom ibraries
|
|
|
|
if options.get("libraries"):
|
|
|
|
installed_conds.append(
|
|
|
|
_install_project_env_custom_libraries(project_env, options)
|
|
|
|
)
|
|
|
|
# declared dependencies
|
|
|
|
if not installed_conds:
|
|
|
|
installed_conds = [
|
2022-03-08 14:58:01 +02:00
|
|
|
_install_project_env_platform(project_env, options),
|
|
|
|
_install_project_env_libraries(project_env, options),
|
|
|
|
]
|
2022-03-14 12:18:05 +02:00
|
|
|
return any(installed_conds)
|
2022-03-08 14:58:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
def _install_project_env_platform(project_env, options):
|
|
|
|
config = ProjectConfig.get_instance()
|
|
|
|
pm = PlatformPackageManager()
|
|
|
|
if options.get("silent"):
|
|
|
|
pm.set_log_level(logging.WARN)
|
2022-03-14 12:18:05 +02:00
|
|
|
spec = config.get(f"env:{project_env}", "platform")
|
|
|
|
if not spec:
|
|
|
|
return False
|
|
|
|
already_up_to_date = not options.get("force")
|
|
|
|
if not pm.get_package(spec):
|
2022-03-08 14:58:01 +02:00
|
|
|
already_up_to_date = False
|
2022-03-14 12:18:05 +02:00
|
|
|
PlatformPackageManager().install(
|
|
|
|
spec,
|
|
|
|
project_env=project_env,
|
|
|
|
project_targets=options.get("project_targets"),
|
2022-03-19 18:12:36 +02:00
|
|
|
skip_dependencies=options.get("skip_dependencies"),
|
2022-03-14 12:18:05 +02:00
|
|
|
force=options.get("force"),
|
|
|
|
)
|
|
|
|
return not already_up_to_date
|
2022-03-08 14:58:01 +02:00
|
|
|
|
|
|
|
|
2022-03-14 12:18:05 +02:00
|
|
|
def _install_project_env_custom_platforms(project_env, options):
|
|
|
|
already_up_to_date = not options.get("force")
|
|
|
|
pm = PlatformPackageManager()
|
|
|
|
if not options.get("silent"):
|
|
|
|
pm.set_log_level(logging.DEBUG)
|
|
|
|
for spec in options.get("platforms"):
|
|
|
|
if not pm.get_package(spec):
|
2022-03-08 14:58:01 +02:00
|
|
|
already_up_to_date = False
|
2022-03-14 12:18:05 +02:00
|
|
|
pm.install(
|
|
|
|
spec,
|
2022-03-08 14:58:01 +02:00
|
|
|
project_env=project_env,
|
|
|
|
project_targets=options.get("project_targets"),
|
2022-03-19 18:12:36 +02:00
|
|
|
skip_dependencies=options.get("skip_dependencies"),
|
2022-03-08 14:58:01 +02:00
|
|
|
force=options.get("force"),
|
|
|
|
)
|
|
|
|
return not already_up_to_date
|
|
|
|
|
|
|
|
|
2022-03-14 12:18:05 +02:00
|
|
|
def _install_project_env_custom_tools(project_env, options):
|
|
|
|
already_up_to_date = not options.get("force")
|
|
|
|
tm = ToolPackageManager()
|
|
|
|
if not options.get("silent"):
|
|
|
|
tm.set_log_level(logging.DEBUG)
|
2022-03-14 13:37:47 +02:00
|
|
|
specs_to_save = []
|
|
|
|
for tool in options.get("tools"):
|
|
|
|
spec = PackageSpec(tool)
|
2022-03-14 12:18:05 +02:00
|
|
|
if not tm.get_package(spec):
|
|
|
|
already_up_to_date = False
|
2022-03-14 13:37:47 +02:00
|
|
|
pkg = tm.install(
|
2022-03-14 12:18:05 +02:00
|
|
|
spec,
|
|
|
|
skip_dependencies=options.get("skip_dependencies"),
|
|
|
|
force=options.get("force"),
|
|
|
|
)
|
2022-03-16 16:18:59 +02:00
|
|
|
specs_to_save.append(pkg_to_save_spec(pkg, spec))
|
2022-03-14 13:37:47 +02:00
|
|
|
if not options.get("no_save") and specs_to_save:
|
|
|
|
save_project_dependencies(
|
|
|
|
os.getcwd(),
|
|
|
|
specs_to_save,
|
|
|
|
scope="platform_packages",
|
|
|
|
action="add",
|
|
|
|
environments=[project_env],
|
|
|
|
)
|
2022-03-14 12:18:05 +02:00
|
|
|
return not already_up_to_date
|
|
|
|
|
|
|
|
|
2022-03-08 14:58:01 +02:00
|
|
|
def _install_project_env_libraries(project_env, options):
|
2022-04-02 22:30:35 +03:00
|
|
|
_uninstall_project_unused_libdeps(project_env, options)
|
2022-03-08 14:58:01 +02:00
|
|
|
already_up_to_date = not options.get("force")
|
|
|
|
config = ProjectConfig.get_instance()
|
2022-04-01 17:25:40 +03:00
|
|
|
env_lm = LibraryPackageManager(
|
2022-03-08 14:58:01 +02:00
|
|
|
os.path.join(config.get("platformio", "libdeps_dir"), project_env)
|
|
|
|
)
|
2022-04-01 17:25:40 +03:00
|
|
|
private_lm = LibraryPackageManager(
|
|
|
|
os.path.join(config.get("platformio", "lib_dir"))
|
|
|
|
)
|
2022-03-08 14:58:01 +02:00
|
|
|
if options.get("silent"):
|
2022-04-01 17:25:40 +03:00
|
|
|
env_lm.set_log_level(logging.WARN)
|
|
|
|
private_lm.set_log_level(logging.WARN)
|
2022-04-21 18:11:49 +03:00
|
|
|
|
|
|
|
lib_deps = config.get(f"env:{project_env}", "lib_deps")
|
|
|
|
if "__test" in options.get("project_targets", []):
|
|
|
|
test_runner = TestRunnerFactory.new(TestSuite(project_env, "*"), config)
|
|
|
|
lib_deps.extend(test_runner.EXTRA_LIB_DEPS or [])
|
|
|
|
|
|
|
|
for library in lib_deps:
|
2022-03-09 19:07:11 +02:00
|
|
|
spec = PackageSpec(library)
|
2022-03-08 14:58:01 +02:00
|
|
|
# skip built-in dependencies
|
2022-03-09 19:07:11 +02:00
|
|
|
if not spec.external and not spec.owner:
|
2022-03-08 14:58:01 +02:00
|
|
|
continue
|
2022-04-01 17:25:40 +03:00
|
|
|
if not env_lm.get_package(spec):
|
2022-03-08 14:58:01 +02:00
|
|
|
already_up_to_date = False
|
2022-04-01 17:25:40 +03:00
|
|
|
env_lm.install(
|
2022-03-08 14:58:01 +02:00
|
|
|
spec,
|
|
|
|
skip_dependencies=options.get("skip_dependencies"),
|
|
|
|
force=options.get("force"),
|
|
|
|
)
|
2022-04-21 18:11:49 +03:00
|
|
|
|
2022-04-02 22:30:35 +03:00
|
|
|
# install dependencies from the private libraries
|
2022-04-01 17:25:40 +03:00
|
|
|
for pkg in private_lm.get_installed():
|
|
|
|
_install_project_private_library_deps(pkg, private_lm, env_lm, options)
|
2022-04-21 18:11:49 +03:00
|
|
|
|
2022-03-08 14:58:01 +02:00
|
|
|
return not already_up_to_date
|
2022-03-09 19:07:11 +02:00
|
|
|
|
|
|
|
|
2022-04-02 22:30:35 +03:00
|
|
|
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():
|
2022-04-03 10:54:23 +03:00
|
|
|
prev_lib_deps = set(
|
|
|
|
integrity_dat.read_text(encoding="utf-8").strip().split("\n")
|
|
|
|
)
|
2022-04-02 22:30:35 +03:00
|
|
|
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")
|
|
|
|
|
|
|
|
|
2022-04-01 17:25:40 +03:00
|
|
|
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)
|
|
|
|
# skip built-in dependencies
|
|
|
|
if not spec.external and not spec.owner:
|
|
|
|
continue
|
|
|
|
pkg = private_lm.get_package(spec)
|
|
|
|
if not pkg and not env_lm.get_package(spec):
|
|
|
|
pkg = env_lm.install(
|
|
|
|
spec,
|
|
|
|
skip_dependencies=True,
|
|
|
|
force=options.get("force"),
|
|
|
|
)
|
2022-04-02 16:38:54 +03:00
|
|
|
if not pkg:
|
|
|
|
continue
|
2022-04-01 17:25:40 +03:00
|
|
|
_install_project_private_library_deps(pkg, private_lm, env_lm, options)
|
|
|
|
|
|
|
|
|
2022-03-14 12:18:05 +02:00
|
|
|
def _install_project_env_custom_libraries(project_env, options):
|
|
|
|
already_up_to_date = not options.get("force")
|
|
|
|
config = ProjectConfig.get_instance()
|
|
|
|
lm = LibraryPackageManager(
|
|
|
|
os.path.join(config.get("platformio", "libdeps_dir"), project_env)
|
|
|
|
)
|
|
|
|
if not options.get("silent"):
|
|
|
|
lm.set_log_level(logging.DEBUG)
|
|
|
|
specs_to_save = []
|
|
|
|
for library in options.get("libraries") or []:
|
|
|
|
spec = PackageSpec(library)
|
|
|
|
if not lm.get_package(spec):
|
|
|
|
already_up_to_date = False
|
|
|
|
pkg = lm.install(
|
|
|
|
spec,
|
|
|
|
skip_dependencies=options.get("skip_dependencies"),
|
|
|
|
force=options.get("force"),
|
|
|
|
)
|
2022-03-16 16:18:59 +02:00
|
|
|
specs_to_save.append(pkg_to_save_spec(pkg, spec))
|
2022-03-14 12:18:05 +02:00
|
|
|
if not options.get("no_save") and specs_to_save:
|
|
|
|
save_project_dependencies(
|
|
|
|
os.getcwd(),
|
|
|
|
specs_to_save,
|
|
|
|
scope="lib_deps",
|
|
|
|
action="add",
|
|
|
|
environments=[project_env],
|
|
|
|
)
|
|
|
|
return not already_up_to_date
|