Merge branch 'release/v6.1.14'

This commit is contained in:
Ivan Kravets
2024-03-21 21:11:24 +02:00
53 changed files with 458 additions and 244 deletions

View File

@ -7,6 +7,7 @@ Release Notes
.. |INTERPOLATION| replace:: `Interpolation of Values <https://docs.platformio.org/en/latest/projectconf/interpolation.html>`__
.. |UNITTESTING| replace:: `Unit Testing <https://docs.platformio.org/en/latest/advanced/unit-testing/index.html>`__
.. |DEBUGGING| replace:: `Debugging <https://docs.platformio.org/en/latest/plus/debugging.html>`__
.. |STATICCODEANALYSIS| replace:: `Static Code Analysis <https://docs.platformio.org/en/latest/advanced/static-code-analysis/index.html>`__
.. _release_notes_6:
@ -17,6 +18,19 @@ Unlock the true potential of embedded software development with
PlatformIO's collaborative ecosystem, embracing declarative principles,
test-driven methodologies, and modern toolchains for unrivaled success.
6.1.14 (2024-03-21)
~~~~~~~~~~~~~~~~~~~
* Introduced the ``--json-output`` option to the `pio test <https://docs.platformio.org/en/latest/core/userguide/cmd_test.html>`__ command, enabling users to generate test results in the JSON format
* Upgraded the build engine to the latest version of SCons (4.7.0) to improve build performance, reliability, and compatibility with other tools and systems (`release notes <https://github.com/SCons/scons/releases/tag/4.7.0>`__)
* Broadened version support for the ``pyelftools`` dependency, enabling compatibility with lower versions and facilitating integration with a wider range of third-party tools (`issue #4834 <https://github.com/platformio/platformio-core/issues/4834>`_)
* Addressed an issue where passing a relative path (``--project-dir``) to the `pio project init <https://docs.platformio.org/en/latest/core/userguide/project/cmd_init.html>`__ command resulted in an error (`issue #4847 <https://github.com/platformio/platformio-core/issues/4847>`_)
* Enhanced |STATICCODEANALYSIS| to accommodate scenarios where custom ``src_dir`` or ``include_dir`` are located outside the project folder (`pull #4874 <https://github.com/platformio/platformio-core/pull/4874>`_)
* Corrected the validation of ``symlink://`` `package specifications <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_install.html#local-folder>`__ , resolving an issue that caused the package manager to repeatedly reinstall dependencies (`pull #4870 <https://github.com/platformio/platformio-core/pull/4870>`_)
* Resolved an issue related to the relative package path in the `pio pkg publish <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_publish.html>`__ command
* Resolved an issue where the |LDF| selected an incorrect library version (`issue #4860 <https://github.com/platformio/platformio-core/issues/4860>`_)
* Resolved an issue with the ``hexlify`` filter in the `device monitor <https://docs.platformio.org/en/latest/core/userguide/device/cmd_monitor.html>`__ command, ensuring proper representation of characters with Unicode code points higher than 127 (`issue #4732 <https://github.com/platformio/platformio-core/issues/4732>`_)
6.1.13 (2024-01-12)
~~~~~~~~~~~~~~~~~~~

2
docs

Submodule docs updated: 3f02152561...670721e923

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
VERSION = (6, 1, 13)
VERSION = (6, 1, 14)
__version__ = ".".join([str(s) for s in VERSION])
__title__ = "platformio"
@ -38,15 +38,6 @@ __registry_mirror_hosts__ = [
]
__pioremote_endpoint__ = "ssl:host=remote.platformio.org:port=4413"
__core_packages__ = {
"contrib-piohome": "~3.4.2",
"contrib-pioremote": "~1.0.0",
"tool-scons": "~4.40600.0",
"tool-cppcheck": "~1.21100.0",
"tool-clangtidy": "~1.150005.0",
"tool-pvs-studio": "~7.18.0",
}
__check_internet_hosts__ = [
"185.199.110.153", # Github.com
"88.198.170.159", # platformio.org

View File

@ -48,11 +48,13 @@ def team_list_cmd(orgname, json_output):
table_data.append(
(
"Members:",
", ".join(
(member.get("username") for member in team.get("members"))
)
if team.get("members")
else "-",
(
", ".join(
(member.get("username") for member in team.get("members"))
)
if team.get("members")
else "-"
),
)
)
click.echo(tabulate(table_data, tablefmt="plain"))

View File

@ -36,6 +36,8 @@ ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", MODE:="0666", ENV{ID_MM_DEVIC
# QinHeng Electronics HL-340 USB-Serial adapter
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# QinHeng Electronics CH343 USB-Serial adapter
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d3", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# QinHeng Electronics CH9102 USB-Serial adapter
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d4", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
@ -85,6 +87,8 @@ ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="[01]*", MODE:="0666", ENV{ID_MM_DEVI
# AIR32F103
ATTRS{idVendor}=="0d28", ATTRS{idProduct}=="0204", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# STM32 virtual COM port
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
#
# Debuggers
@ -173,4 +177,4 @@ ATTRS{product}=="*CMSIS-DAP*", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID
ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2107", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# Espressif USB JTAG/serial debug unit
ATTRS{idVendor}=="303a", ATTR{idProduct}=="1001", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
ATTRS{idVendor}=="303a", ATTR{idProduct}=="1001", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

View File

@ -54,7 +54,7 @@ def GetBuildType(env):
modes.append("debug")
if "__test" in COMMAND_LINE_TARGETS or env.GetProjectOption("build_type") == "test":
modes.append("test")
return "+".join(modes or ["release"])
return ", ".join(modes or ["release"])
def BuildProgram(env):

View File

@ -309,10 +309,10 @@ class LibBuilderBase:
if not self.dependencies or self._deps_are_processed:
return
self._deps_are_processed = True
for item in self.dependencies:
for dependency in self.dependencies:
found = False
for lb in self.env.GetLibBuilders():
if item["name"] != lb.name:
if not lb.is_dependency_compatible(dependency):
continue
found = True
if lb not in self.depbuilders:
@ -322,9 +322,20 @@ class LibBuilderBase:
if not found and self.verbose:
sys.stderr.write(
"Warning: Ignored `%s` dependency for `%s` "
"library\n" % (item["name"], self.name)
"library\n" % (dependency["name"], self.name)
)
def is_dependency_compatible(self, dependency):
pkg = PackageItem(self.path)
qualifiers = {"name": self.name, "version": self.version}
if pkg.metadata:
qualifiers = {"name": pkg.metadata.name, "version": pkg.metadata.version}
if pkg.metadata.spec and pkg.metadata.spec.owner:
qualifiers["owner"] = pkg.metadata.spec.owner
return PackageCompatibility.from_dependency(
{k: v for k, v in dependency.items() if k in ("owner", "name", "version")}
).is_compatible(PackageCompatibility(**qualifiers))
def get_search_files(self):
return [
os.path.join(self.src_dir, item)

View File

@ -23,10 +23,10 @@ from SCons.Subst import quote_spaces # pylint: disable=import-error
from platformio.compat import IS_WINDOWS, hashlib_encode_data
# There are the next limits depending on a platform:
# - Windows = 8192
# - Windows = 8191
# - Unix = 131072
# We need ~512 characters for compiler and temporary file paths
MAX_LINE_LENGTH = (8192 if IS_WINDOWS else 131072) - 512
MAX_LINE_LENGTH = (8191 if IS_WINDOWS else 131072) - 512
WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)")

View File

@ -75,9 +75,11 @@ def LoadPioPlatform(env):
continue
env.PrependENVPath(
"PATH",
os.path.join(pkg.path, "bin")
if os.path.isdir(os.path.join(pkg.path, "bin"))
else pkg.path,
(
os.path.join(pkg.path, "bin")
if os.path.isdir(os.path.join(pkg.path, "bin"))
else pkg.path
),
)
if (
not IS_WINDOWS

View File

@ -103,10 +103,21 @@ def cli(
"%s: %s" % (k, ", ".join(v) if isinstance(v, list) else v)
)
default_src_filters = [
"+<%s>" % os.path.basename(config.get("platformio", "src_dir")),
"+<%s>" % os.path.basename(config.get("platformio", "include_dir")),
]
default_src_filters = []
for d in (
config.get("platformio", "src_dir"),
config.get("platformio", "include_dir"),
):
try:
default_src_filters.append("+<%s>" % os.path.relpath(d))
except ValueError as exc:
# On Windows if sources are located on a different logical drive
if not json_output and not silent:
click.echo(
"Error: Project cannot be analyzed! The project folder `%s`"
" is located on a different logical drive\n" % d
)
raise exception.ReturnErrorCode(1) from exc
env_src_filters = (
src_filters
@ -122,9 +133,11 @@ def cli(
silent=silent,
src_filters=env_src_filters,
flags=flags or env_options.get("check_flags"),
severity=[DefectItem.SEVERITY_LABELS[DefectItem.SEVERITY_HIGH]]
if silent
else severity or config.get("env:" + envname, "check_severity"),
severity=(
[DefectItem.SEVERITY_LABELS[DefectItem.SEVERITY_HIGH]]
if silent
else severity or config.get("env:" + envname, "check_severity")
),
skip_packages=skip_packages or env_options.get("check_skip_packages"),
platform_packages=env_options.get("platform_packages"),
)
@ -142,9 +155,11 @@ def cli(
result = {"env": envname, "tool": tool, "duration": time()}
rc = ct.check(
on_defect_callback=None
if (json_output or verbose)
else lambda defect: click.echo(repr(defect))
on_defect_callback=(
None
if (json_output or verbose)
else lambda defect: click.echo(repr(defect))
)
)
result["defects"] = ct.get_defects()

View File

@ -19,9 +19,9 @@ import subprocess
import click
from platformio import VERSION, __version__, app, exception
from platformio.dependencies import get_pip_dependencies
from platformio.http import fetch_remote_content
from platformio.package.manager.core import update_core_packages
from platformio.pipdeps import get_pip_dependencies
from platformio.proc import get_pythonexe_path
PYPI_JSON_URL = "https://pypi.org/pypi/platformio/json"

View File

@ -17,6 +17,7 @@
import importlib.util
import inspect
import locale
import os
import shlex
import sys
@ -41,10 +42,14 @@ else:
if sys.version_info >= (3, 9):
from asyncio import to_thread as aio_to_thread
else:
from starlette.concurrency import run_in_threadpool as aio_to_thread
try:
from starlette.concurrency import run_in_threadpool as aio_to_thread
except ImportError:
pass
PY2 = sys.version_info[0] == 2 # DO NOT REMOVE IT. ESP8266/ESP32 depend on it
PY36 = sys.version_info[0:2] == (3, 6)
IS_CYGWIN = sys.platform.startswith("cygwin")
IS_WINDOWS = WINDOWS = sys.platform.startswith("win")
IS_MACOS = sys.platform.startswith("darwin")
@ -132,3 +137,12 @@ def path_to_unicode(path):
and custom device monitor filters
"""
return path
def is_proxy_set(socks=False):
for var in ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"):
value = os.getenv(var, os.getenv(var.lower()))
if not value or (socks and not value.startswith("socks5://")):
continue
return True
return False

View File

@ -148,7 +148,9 @@ class DebugConfigBase: # pylint: disable=too-many-instance-attributes
)
def _load_build_data(self):
data = load_build_metadata(os.getcwd(), self.env_name, cache=True, debug=True)
data = load_build_metadata(
os.getcwd(), self.env_name, cache=True, build_type="debug"
)
if not data:
raise DebugInvalidOptionsError("Could not load a build configuration")
return data
@ -194,9 +196,11 @@ class DebugConfigBase: # pylint: disable=too-many-instance-attributes
cwd=server_package_dir if server_package else None,
executable=result.get("executable"),
arguments=[
a.replace("$PACKAGE_DIR", server_package_dir)
if server_package_dir
else a
(
a.replace("$PACKAGE_DIR", server_package_dir)
if server_package_dir
else a
)
for a in result.get("arguments", [])
],
)

View File

@ -12,11 +12,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import platform
import sys
PY36 = sys.version_info[0:2] == (3, 6)
from platformio.compat import PY36, is_proxy_set
def get_core_dependencies():
return {
"contrib-piohome": "~3.4.2",
"contrib-pioremote": "~1.0.0",
"tool-scons": "~4.40700.0",
"tool-cppcheck": "~1.21100.0",
"tool-clangtidy": "~1.150005.0",
"tool-pvs-studio": "~7.18.0",
}
def get_pip_dependencies():
@ -25,7 +34,7 @@ def get_pip_dependencies():
"click >=8.0.4, <9",
"colorama",
"marshmallow == 3.*",
"pyelftools == 0.30",
"pyelftools >=0.27, <1",
"pyserial == 3.5.*", # keep in sync "device/monitor/terminal.py"
"requests%s == 2.*" % ("[socks]" if is_proxy_set(socks=True) else ""),
"semantic_version == 2.10.*",
@ -35,8 +44,8 @@ def get_pip_dependencies():
home = [
# PIO Home requirements
"ajsonrpc == 1.2.*",
"starlette >=0.19, <0.36",
"uvicorn %s" % ("== 0.16.0" if PY36 else ">=0.16, <0.26"),
"starlette >=0.19, <0.38",
"uvicorn %s" % ("== 0.16.0" if PY36 else ">=0.16, <0.30"),
"wsproto == 1.*",
]
@ -60,12 +69,3 @@ def get_pip_dependencies():
pass
return core + home + extra
def is_proxy_set(socks=False):
for var in ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"):
value = os.getenv(var, os.getenv(var.lower()))
if not value or (socks and not value.startswith("socks5://")):
continue
return True
return False

View File

@ -144,9 +144,9 @@ def list_mdns_services():
if service.properties:
try:
properties = {
k.decode("utf8"): v.decode("utf8")
if isinstance(v, bytes)
else v
k.decode("utf8"): (
v.decode("utf8") if isinstance(v, bytes) else v
)
for k, v in service.properties.items()
}
json.dumps(properties)

View File

@ -58,7 +58,7 @@ from platformio.project.options import ProjectOptions
"--encoding",
help=(
"Set the encoding for the serial port "
"(e.g. hexlify, Latin1, UTF-8) [default=%s]"
"(e.g. hexlify, Latin-1, UTF-8) [default=%s]"
% ProjectOptions["env.monitor_encoding"].default
),
)
@ -125,9 +125,11 @@ def device_monitor_cmd(**options):
options = apply_project_monitor_options(options, project_options)
register_filters(platform=platform, options=options)
options["port"] = SerialPortFinder(
board_config=platform.board_config(project_options.get("board"))
if platform and project_options.get("board")
else None,
board_config=(
platform.board_config(project_options.get("board"))
if platform and project_options.get("board")
else None
),
upload_protocol=project_options.get("upload_protocol"),
ensure_ready=True,
).find(initial_port=options["port"])

View File

@ -25,11 +25,12 @@ from platformio.project.config import ProjectConfig
class DeviceMonitorFilterBase(miniterm.Transform):
def __init__(self, options=None):
"""Called by PlatformIO to pass context"""
miniterm.Transform.__init__(self)
super().__init__()
self.options = options or {}
self.project_dir = self.options.get("project_dir")
self.environment = self.options.get("environment")
self._running_terminal = None
self.config = ProjectConfig.get_instance()
if not self.environment:
@ -47,6 +48,12 @@ class DeviceMonitorFilterBase(miniterm.Transform):
def NAME(self):
raise NotImplementedError("Please declare NAME attribute for the filter class")
def set_running_terminal(self, terminal):
self._running_terminal = terminal
def get_running_terminal(self):
return self._running_terminal
def register_filters(platform=None, options=None):
# project filters

View File

@ -24,12 +24,18 @@ class Hexlify(DeviceMonitorFilterBase):
super().__init__(*args, **kwargs)
self._counter = 0
def set_running_terminal(self, terminal):
# force to Latin-1, issue #4732
if terminal.input_encoding == "UTF-8":
terminal.set_rx_encoding("Latin-1")
super().set_running_terminal(terminal)
def rx(self, text):
result = ""
for b in serial.iterbytes(text):
for c in serial.iterbytes(text):
if (self._counter % 16) == 0:
result += "\n{:04X} | ".format(self._counter)
asciicode = ord(b)
asciicode = ord(c)
if asciicode <= 255:
result += "{:02X} ".format(asciicode)
else:

View File

@ -110,6 +110,12 @@ def new_terminal(options):
term.raw = options["raw"]
term.set_rx_encoding(options["encoding"])
term.set_tx_encoding(options["encoding"])
for ts in (term.tx_transformations, term.rx_transformations):
for t in ts:
try:
t.set_running_terminal(term)
except AttributeError:
pass
return term

View File

@ -372,15 +372,19 @@ class ProjectRPC(BaseRPCHandler):
return dict(
platform=dict(
ownername=platform_pkg.metadata.spec.owner
if platform_pkg.metadata.spec
else None,
ownername=(
platform_pkg.metadata.spec.owner
if platform_pkg.metadata.spec
else None
),
name=platform.name,
title=platform.title,
version=str(platform_pkg.metadata.version),
),
board=platform.board_config(board_id).get_brief_data()
if board_id
else None,
board=(
platform.board_config(board_id).get_brief_data()
if board_id
else None
),
frameworks=frameworks or None,
)

View File

@ -21,8 +21,8 @@ from urllib3.util.retry import Retry
from platformio import __check_internet_hosts__, app, util
from platformio.cache import ContentCache, cleanup_content_cache
from platformio.compat import is_proxy_set
from platformio.exception import PlatformioException, UserSideException
from platformio.pipdeps import is_proxy_set
__default_requests_timeout__ = (10, None) # (connect, read)
@ -63,9 +63,11 @@ class HTTPSession(requests.Session):
kwargs["timeout"] = __default_requests_timeout__
return super().request(
method,
url
if url.startswith("http") or not self._x_base_url
else urljoin(self._x_base_url, url),
(
url
if url.startswith("http") or not self._x_base_url
else urljoin(self._x_base_url, url)
),
*args,
**kwargs
)

View File

@ -222,9 +222,11 @@ def _install_project_env_libraries(project_env, options):
env_lm = LibraryPackageManager(
os.path.join(config.get("platformio", "libdeps_dir"), project_env),
compatibility=PackageCompatibility(**compatibility_qualifiers)
if compatibility_qualifiers
else None,
compatibility=(
PackageCompatibility(**compatibility_qualifiers)
if compatibility_qualifiers
else None
),
)
private_lm = LibraryPackageManager(
os.path.join(config.get("platformio", "lib_dir"))

View File

@ -86,6 +86,7 @@ def package_publish_cmd( # pylint: disable=too-many-arguments, too-many-locals
package, owner, typex, released_at, private, notify, no_interactive, non_interactive
):
click.secho("Preparing a package...", fg="cyan")
package = os.path.abspath(package)
no_interactive = no_interactive or non_interactive
owner = owner or AccountClient().get_logged_username()
do_not_pack = (

View File

@ -65,10 +65,12 @@ def print_search_item(item):
click.echo(
"%s%s • Published on %s"
% (
item["type"].capitalize()
if item["tier"] == "community"
else click.style(
("%s %s" % (item["tier"], item["type"])).title(), bold=True
(
item["type"].capitalize()
if item["tier"] == "community"
else click.style(
("%s %s" % (item["tier"], item["type"])).title(), bold=True
)
),
item["version"]["name"],
util.parse_datetime(item["version"]["released_at"]).strftime("%c"),

View File

@ -98,9 +98,13 @@ class PackageManagerInstallMixin:
else:
pkg = self.install_from_registry(
spec,
search_qualifiers=compatibility.to_search_qualifiers()
if compatibility
else None,
search_qualifiers=(
compatibility.to_search_qualifiers(
["platforms", "frameworks", "authors"]
)
if compatibility
else None
),
)
if not pkg or not pkg.metadata:

View File

@ -280,11 +280,15 @@ class BasePackageManager( # pylint: disable=too-many-public-methods,too-many-in
# external "URL" mismatch
if spec.external:
# local folder mismatch
if os.path.abspath(spec.uri) == os.path.abspath(pkg.path) or (
# local/symlinked folder mismatch
check_conds = [
os.path.abspath(spec.uri) == os.path.abspath(pkg.path),
spec.uri.startswith("file://")
and os.path.abspath(pkg.path) == os.path.abspath(spec.uri[7:])
):
and os.path.abspath(pkg.path) == os.path.abspath(spec.uri[7:]),
spec.uri.startswith("symlink://")
and os.path.abspath(pkg.path) == os.path.abspath(spec.uri[10:]),
]
if any(check_conds):
return True
if spec.uri != pkg.metadata.spec.uri:
return False

View File

@ -14,7 +14,8 @@
import os
from platformio import __core_packages__, exception
from platformio import exception
from platformio.dependencies import get_core_dependencies
from platformio.package.exception import UnknownPackageError
from platformio.package.manager.tool import ToolPackageManager
from platformio.package.meta import PackageSpec
@ -23,7 +24,7 @@ from platformio.package.meta import PackageSpec
def get_installed_core_packages():
result = []
pm = ToolPackageManager()
for name, requirements in __core_packages__.items():
for name, requirements in get_core_dependencies().items():
spec = PackageSpec(owner="platformio", name=name, requirements=requirements)
pkg = pm.get_package(spec)
if pkg:
@ -32,11 +33,11 @@ def get_installed_core_packages():
def get_core_package_dir(name, spec=None, auto_install=True):
if name not in __core_packages__:
if name not in get_core_dependencies():
raise exception.PlatformioException("Please upgrade PlatformIO Core")
pm = ToolPackageManager()
spec = spec or PackageSpec(
owner="platformio", name=name, requirements=__core_packages__[name]
owner="platformio", name=name, requirements=get_core_dependencies()[name]
)
pkg = pm.get_package(spec)
if pkg:
@ -50,7 +51,7 @@ def get_core_package_dir(name, spec=None, auto_install=True):
def update_core_packages():
pm = ToolPackageManager()
for name, requirements in __core_packages__.items():
for name, requirements in get_core_dependencies().items():
spec = PackageSpec(owner="platformio", name=name, requirements=requirements)
try:
pm.update(spec, spec)
@ -65,7 +66,7 @@ def remove_unnecessary_core_packages(dry_run=False):
pm = ToolPackageManager()
best_pkg_versions = {}
for name, requirements in __core_packages__.items():
for name, requirements in get_core_dependencies().items():
spec = PackageSpec(owner="platformio", name=name, requirements=requirements)
pkg = pm.get_package(spec)
if not pkg:

View File

@ -294,9 +294,11 @@ class BaseManifestParser:
if not matched_files:
continue
result[root] = dict(
name="Examples"
if root == examples_dir
else os.path.relpath(root, examples_dir),
name=(
"Examples"
if root == examples_dir
else os.path.relpath(root, examples_dir)
),
base=os.path.relpath(root, package_dir),
files=matched_files,
)

View File

@ -276,7 +276,7 @@ class ManifestSchema(BaseSchema):
@staticmethod
@memoized(expire="1h")
def load_spdx_licenses():
version = "3.22"
version = "3.23"
spdx_data_url = (
"https://raw.githubusercontent.com/spdx/license-list-data/"
f"v{version}/json/licenses.json"

View File

@ -65,7 +65,14 @@ class PackageType:
class PackageCompatibility:
KNOWN_QUALIFIERS = ("platforms", "frameworks", "authors")
KNOWN_QUALIFIERS = (
"owner",
"name",
"version",
"platforms",
"frameworks",
"authors",
)
@classmethod
def from_dependency(cls, dependency):
@ -89,19 +96,45 @@ class PackageCompatibility:
def __repr__(self):
return "PackageCompatibility <%s>" % self.qualifiers
def to_search_qualifiers(self):
return self.qualifiers
def to_search_qualifiers(self, fields=None):
result = {}
for name, value in self.qualifiers.items():
if not fields or name in fields:
result[name] = value
return result
def is_compatible(self, other):
assert isinstance(other, PackageCompatibility)
for key, value in self.qualifiers.items():
for key, current_value in self.qualifiers.items():
other_value = other.qualifiers.get(key)
if not value or not other_value:
if not current_value or not other_value:
continue
if not items_in_list(value, other_value):
if any(isinstance(v, list) for v in (current_value, other_value)):
if not items_in_list(current_value, other_value):
return False
continue
if key == "version":
if not self._compare_versions(current_value, other_value):
return False
continue
if current_value != other_value:
return False
return True
def _compare_versions(self, current, other):
if current == other:
return True
try:
version = (
other
if isinstance(other, semantic_version.Version)
else cast_version_to_semver(other)
)
return version in semantic_version.SimpleSpec(current)
except ValueError:
pass
return False
class PackageOutdatedResult:
UPDATE_INCREMENT_MAJOR = "major"
@ -485,9 +518,11 @@ class PackageItem:
def __eq__(self, other):
conds = [
os.path.realpath(self.path) == os.path.realpath(other.path)
if self.path and other.path
else self.path == other.path,
(
os.path.realpath(self.path) == os.path.realpath(other.path)
if self.path and other.path
else self.path == other.path
),
self.metadata == other.metadata,
]
return all(conds)

View File

@ -13,6 +13,7 @@
# limitations under the License.
import os
import sys
from tarfile import open as tarfile_open
from time import mktime
from zipfile import ZipFile
@ -82,19 +83,23 @@ class TARArchiver(BaseArchiver):
).startswith(base)
def extract_item(self, item, dest_dir):
if sys.version_info >= (3, 12):
self._afo.extract(item, dest_dir, filter="data")
return self.after_extract(item, dest_dir)
# apply custom security logic
dest_dir = self.resolve_path(dest_dir)
bad_conds = [
self.is_bad_path(item.name, dest_dir),
self.is_link(item) and self.is_bad_link(item, dest_dir),
]
if not any(bad_conds):
super().extract_item(item, dest_dir)
else:
click.secho(
if any(bad_conds):
return click.secho(
"Blocked insecure item `%s` from TAR archive" % item.name,
fg="red",
err=True,
)
return super().extract_item(item, dest_dir)
class ZIPArchiver(BaseArchiver):

View File

@ -116,9 +116,9 @@ class PlatformRunMixin:
args,
stdout=proc.BuildAsyncPipe(
line_callback=self._on_stdout_line,
data_callback=lambda data: None
if self.silent
else _write_and_flush(sys.stdout, data),
data_callback=lambda data: (
None if self.silent else _write_and_flush(sys.stdout, data)
),
),
stderr=proc.BuildAsyncPipe(
line_callback=self._on_stderr_line,

View File

@ -82,9 +82,11 @@ def lint_configuration(json_output=False):
(
click.style(error["type"], fg="red"),
error["message"],
error.get("source", "") + (f":{error.get('lineno')}")
if "lineno" in error
else "",
(
error.get("source", "") + (f":{error.get('lineno')}")
if "lineno" in error
else ""
),
)
for error in errors
],

View File

@ -79,6 +79,7 @@ def project_init_cmd(
env_prefix,
silent,
):
project_dir = os.path.abspath(project_dir)
is_new_project = not is_platformio_project(project_dir)
if is_new_project:
if not silent:
@ -223,7 +224,7 @@ def init_lib_readme(lib_dir):
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
The source code of each library should be placed in an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:

View File

@ -131,27 +131,27 @@ def compute_project_checksum(config):
return checksum.hexdigest()
def load_build_metadata(project_dir, env_or_envs, cache=False, debug=False):
def load_build_metadata(project_dir, env_or_envs, cache=False, build_type=None):
assert env_or_envs
env_names = env_or_envs
if not isinstance(env_names, list):
env_names = [env_names]
with fs.cd(project_dir):
result = _get_cached_build_metadata(project_dir, env_names) if cache else {}
result = _get_cached_build_metadata(env_names) if cache else {}
# incompatible build-type data
for name in list(result.keys()):
build_type = result[name].get("build_type", "")
outdated_conds = [
not build_type,
debug and "debug" not in build_type,
not debug and "debug" in build_type,
]
if any(outdated_conds):
del result[name]
for env_name in list(result.keys()):
if build_type is None:
build_type = ProjectConfig.get_instance().get(
f"env:{env_name}", "build_type"
)
if result[env_name].get("build_type", "") != build_type:
del result[env_name]
missed_env_names = set(env_names) - set(result.keys())
if missed_env_names:
result.update(_load_build_metadata(project_dir, missed_env_names, debug))
result.update(
_load_build_metadata(project_dir, missed_env_names, build_type)
)
if not isinstance(env_or_envs, list) and env_or_envs in result:
return result[env_or_envs]
@ -162,14 +162,16 @@ def load_build_metadata(project_dir, env_or_envs, cache=False, debug=False):
load_project_ide_data = load_build_metadata
def _load_build_metadata(project_dir, env_names, debug=False):
def _load_build_metadata(project_dir, env_names, build_type=None):
# pylint: disable=import-outside-toplevel
from platformio import app
from platformio.run.cli import cli as cmd_run
args = ["--project-dir", project_dir, "--target", "__idedata"]
if debug:
if build_type == "debug":
args.extend(["--target", "__debug"])
# if build_type == "test":
# args.extend(["--target", "__test"])
for name in env_names:
args.extend(["-e", name])
app.set_session_var("pause_telemetry", True)
@ -181,16 +183,16 @@ def _load_build_metadata(project_dir, env_names, debug=False):
raise result.exception
if '"includes":' not in result.output:
raise exception.UserSideException(result.output)
return _get_cached_build_metadata(project_dir, env_names)
return _get_cached_build_metadata(env_names)
def _get_cached_build_metadata(project_dir, env_names):
build_dir = ProjectConfig.get_instance(
os.path.join(project_dir, "platformio.ini")
).get("platformio", "build_dir")
def _get_cached_build_metadata(env_names):
build_dir = ProjectConfig.get_instance().get("platformio", "build_dir")
result = {}
for name in env_names:
if not os.path.isfile(os.path.join(build_dir, name, "idedata.json")):
for env_name in env_names:
if not os.path.isfile(os.path.join(build_dir, env_name, "idedata.json")):
continue
result[name] = fs.load_json(os.path.join(build_dir, name, "idedata.json"))
result[env_name] = fs.load_json(
os.path.join(build_dir, env_name, "idedata.json")
)
return result

View File

@ -91,9 +91,11 @@ class ProjectGenerator:
"default_debug_env_name": get_default_debug_env(self.config),
"env_name": self.env_name,
"user_home_dir": os.path.abspath(fs.expanduser("~")),
"platformio_path": sys.argv[0]
if os.path.isfile(sys.argv[0])
else where_is_program("platformio"),
"platformio_path": (
sys.argv[0]
if os.path.isfile(sys.argv[0])
else where_is_program("platformio")
),
"env_path": os.getenv("PATH"),
"env_pathsep": os.pathsep,
}

View File

@ -549,7 +549,7 @@ ProjectOptions = OrderedDict(
ConfigEnvOption(
group="monitor",
name="monitor_encoding",
description="Custom encoding (e.g. hexlify, Latin1, UTF-8)",
description="Custom encoding (e.g. hexlify, Latin-1, UTF-8)",
default="UTF-8",
),
# Library

View File

@ -41,9 +41,11 @@ def access_list_cmd(owner, urn_type, json_output): # pylint: disable=unused-arg
table_data.append(
(
"Access:",
click.style("Private", fg="red")
if resource.get("private", False)
else "Public",
(
click.style("Private", fg="red")
if resource.get("private", False)
else "Public"
),
)
)
table_data.append(

View File

@ -54,9 +54,11 @@ class RegistryFileMirrorIterator:
"head",
self._url_parts.path,
allow_redirects=False,
params=dict(bypass=",".join(self._visited_mirrors))
if self._visited_mirrors
else None,
params=(
dict(bypass=",".join(self._visited_mirrors))
if self._visited_mirrors
else None
),
x_with_authorization=RegistryClient.allowed_private_packages(),
)
stop_conditions = [

View File

@ -123,9 +123,11 @@ class DeviceMonitorClient( # pylint: disable=too-many-instance-attributes
index=i + 1,
host=device[0] + ":" if len(result) > 1 else "",
port=device[1]["port"],
description=device[1]["description"]
if device[1]["description"] != "n/a"
else "",
description=(
device[1]["description"]
if device[1]["description"] != "n/a"
else ""
),
)
)
device_index = click.prompt(

View File

@ -14,6 +14,7 @@
import os
import shutil
import subprocess
import click
@ -79,6 +80,7 @@ from platformio.test.runners.factory import TestRunnerFactory
help="A program argument (multiple are allowed)",
)
@click.option("--list-tests", is_flag=True)
@click.option("--json-output", is_flag=True)
@click.option("--json-output-path", type=click.Path())
@click.option("--junit-output-path", type=click.Path())
@click.option(
@ -105,6 +107,7 @@ def cli( # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
monitor_dtr,
program_args,
list_tests,
json_output,
json_output_path,
junit_output_path,
verbose,
@ -156,6 +159,7 @@ def cli( # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
stdout_report.generate(verbose=verbose or list_tests)
for output_format, output_path in [
("json", subprocess.STDOUT if json_output else None),
("json", json_output_path),
("junit", junit_output_path),
]:

View File

@ -15,6 +15,7 @@
import datetime
import json
import os
import subprocess
import click
@ -24,6 +25,9 @@ from platformio.test.result import TestStatus
class JsonTestReport(TestReportBase):
def generate(self, output_path, verbose=False):
if output_path == subprocess.STDOUT:
return click.echo("\n\n" + json.dumps(self.to_json()))
if os.path.isdir(output_path):
output_path = os.path.join(
output_path,
@ -40,6 +44,8 @@ class JsonTestReport(TestReportBase):
if verbose:
click.secho(f"Saved JSON report to the {output_path}", fg="green")
return True
def to_json(self):
result = dict(
version="1.0",
@ -62,11 +68,13 @@ class JsonTestReport(TestReportBase):
test_dir=test_suite.test_dir,
status=test_suite.status.name,
duration=test_suite.duration,
timestamp=datetime.datetime.fromtimestamp(test_suite.timestamp).strftime(
"%Y-%m-%dT%H:%M:%S"
)
if test_suite.timestamp
else None,
timestamp=(
datetime.datetime.fromtimestamp(test_suite.timestamp).strftime(
"%Y-%m-%dT%H:%M:%S"
)
if test_suite.timestamp
else None
),
testcase_nums=len(test_suite.cases),
error_nums=test_suite.get_status_nums(TestStatus.ERRORED),
failure_nums=test_suite.get_status_nums(TestStatus.FAILED),

View File

@ -184,10 +184,6 @@ void unityOutputComplete(void) { unittest_uart_end(); }
),
)
def __init__(self, *args, **kwargs):
"""Delete when Unity > 2.5.2 is released"""
super().__init__(*args, **kwargs)
def get_unity_framework_config(self):
if not self.platform.is_embedded():
return self.UNITY_FRAMEWORK_CONFIG["native"]

View File

@ -23,7 +23,7 @@ from platformio import (
__url__,
__version__,
)
from platformio.pipdeps import get_pip_dependencies
from platformio.dependencies import get_pip_dependencies
setup(
name=__title__,

View File

@ -18,7 +18,8 @@ import os
import pytest
from platformio import __core_packages__, fs
from platformio import fs
from platformio.dependencies import get_core_dependencies
from platformio.package.commands.install import package_install_cmd
from platformio.package.manager.library import LibraryPackageManager
from platformio.package.manager.platform import PlatformPackageManager
@ -177,7 +178,7 @@ def test_baremetal_project(
),
]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("tool-scons@%s" % __core_packages__["tool-scons"][1:]),
PackageSpec("tool-scons@%s" % get_core_dependencies()["tool-scons"][1:]),
PackageSpec("toolchain-atmelavr@1.70300.191015"),
]
@ -210,7 +211,7 @@ def test_project(
]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("framework-arduino-avr-attiny@1.5.2"),
PackageSpec("tool-scons@%s" % __core_packages__["tool-scons"][1:]),
PackageSpec("tool-scons@%s" % get_core_dependencies()["tool-scons"][1:]),
PackageSpec("toolchain-atmelavr@1.70300.191015"),
]
assert config.get("env:devkit", "lib_deps") == [

View File

@ -24,7 +24,7 @@ PROJECT_OUTDATED_CONFIG_TPL = """
platform = platformio/atmelavr@^2
framework = arduino
board = attiny88
lib_deps = milesburton/DallasTemperature@~3.8.0
lib_deps = milesburton/DallasTemperature@~3.9.0
"""
PROJECT_UPDATED_CONFIG_TPL = """
@ -32,7 +32,7 @@ PROJECT_UPDATED_CONFIG_TPL = """
platform = platformio/atmelavr@<4
framework = arduino
board = attiny88
lib_deps = milesburton/DallasTemperature@^3.8.0
lib_deps = milesburton/DallasTemperature@^3.9.0
"""
@ -56,7 +56,7 @@ def test_project(clirunner, validate_cliresult, isolated_pio_core, tmp_path):
re.MULTILINE,
)
assert re.search(
r"^DallasTemperature\s+3\.8\.1\s+3\.\d+\.\d+\s+3\.\d+\.\d+\s+Library\s+devkit",
r"^DallasTemperature\s+3\.\d\.1\s+3\.\d+\.\d+\s+3\.\d+\.\d+\s+Library\s+devkit",
result.output,
re.MULTILINE,
)

View File

@ -16,7 +16,8 @@
import os
from platformio import __core_packages__, fs
from platformio import fs
from platformio.dependencies import get_core_dependencies
from platformio.package.commands.install import package_install_cmd
from platformio.package.commands.update import package_update_cmd
from platformio.package.exception import UnknownPackageError
@ -26,12 +27,14 @@ from platformio.package.manager.tool import ToolPackageManager
from platformio.package.meta import PackageSpec
from platformio.project.config import ProjectConfig
DALLASTEMPERATURE_LATEST_VERSION = "3.11.0"
PROJECT_OUTDATED_CONFIG_TPL = """
[env:devkit]
platform = platformio/atmelavr@^2
framework = arduino
board = attiny88
lib_deps = milesburton/DallasTemperature@~3.8.0
lib_deps = milesburton/DallasTemperature@^3.8.0
"""
PROJECT_UPDATED_CONFIG_TPL = """
@ -162,7 +165,7 @@ def test_project(
os.path.join(config.get("platformio", "libdeps_dir"), "devkit")
)
assert pkgs_to_specs(lm.get_installed()) == [
PackageSpec("DallasTemperature@3.8.1"),
PackageSpec(f"DallasTemperature@{DALLASTEMPERATURE_LATEST_VERSION}"),
PackageSpec(
"OneWire@%s" % get_pkg_latest_version("paulstoffregen/OneWire")
),
@ -172,11 +175,11 @@ def test_project(
]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("framework-arduino-avr-attiny@1.3.2"),
PackageSpec("tool-scons@%s" % __core_packages__["tool-scons"][1:]),
PackageSpec("tool-scons@%s" % get_core_dependencies()["tool-scons"][1:]),
PackageSpec("toolchain-atmelavr@1.50400.190710"),
]
assert config.get("env:devkit", "lib_deps") == [
"milesburton/DallasTemperature@~3.8.0"
"milesburton/DallasTemperature@^3.8.0"
]
# update packages
@ -202,7 +205,7 @@ def test_project(
]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("framework-arduino-avr-attiny@1.3.2"),
PackageSpec("tool-scons@%s" % __core_packages__["tool-scons"][1:]),
PackageSpec("tool-scons@%s" % get_core_dependencies()["tool-scons"][1:]),
PackageSpec("toolchain-atmelavr@1.70300.191015"),
PackageSpec("toolchain-atmelavr@1.50400.190710"),
]
@ -227,7 +230,7 @@ def test_custom_project_libraries(
project_dir = tmp_path / "project"
project_dir.mkdir()
(project_dir / "platformio.ini").write_text(PROJECT_OUTDATED_CONFIG_TPL)
spec = "milesburton/DallasTemperature@~3.8.0"
spec = "milesburton/DallasTemperature@^3.8.0"
result = clirunner.invoke(
package_install_cmd,
["-d", str(project_dir), "-e", "devkit", "-l", spec],
@ -240,7 +243,7 @@ def test_custom_project_libraries(
os.path.join(config.get("platformio", "libdeps_dir"), "devkit")
)
assert pkgs_to_specs(lm.get_installed()) == [
PackageSpec("DallasTemperature@3.8.1"),
PackageSpec(f"DallasTemperature@{DALLASTEMPERATURE_LATEST_VERSION}"),
PackageSpec(
"OneWire@%s" % get_pkg_latest_version("paulstoffregen/OneWire")
),

View File

@ -803,3 +803,49 @@ check_src_filters =
assert errors + warnings + style == EXPECTED_DEFECTS
assert "test.cpp" in result.output
assert "main.cpp" not in result.output
def test_check_sources_in_project_root(clirunner, validate_cliresult, tmpdir_factory):
tmpdir = tmpdir_factory.mktemp("project")
config = (
"""
[platformio]
src_dir = ./
"""
+ DEFAULT_CONFIG
)
tmpdir.join("platformio.ini").write(config)
tmpdir.join("main.cpp").write(TEST_CODE)
tmpdir.mkdir("spi").join("uart.cpp").write(TEST_CODE)
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
validate_cliresult(result)
errors, warnings, style = count_defects(result.output)
assert result.exit_code == 0
assert errors + warnings + style == EXPECTED_DEFECTS * 2
def test_check_sources_in_external_dir(clirunner, validate_cliresult, tmpdir_factory):
tmpdir = tmpdir_factory.mktemp("project")
external_src_dir = tmpdir_factory.mktemp("external_src_dir")
config = (
f"""
[platformio]
src_dir = {external_src_dir}
"""
+ DEFAULT_CONFIG
)
tmpdir.join("platformio.ini").write(config)
external_src_dir.join("main.cpp").write(TEST_CODE)
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
validate_cliresult(result)
errors, warnings, style = count_defects(result.output)
assert result.exit_code == 0
assert errors + warnings + style == EXPECTED_DEFECTS

View File

@ -15,6 +15,7 @@
import json
import os
from platformio import fs
from platformio.commands.boards import cli as cmd_boards
from platformio.project.commands.init import project_init_cmd
from platformio.project.config import ProjectConfig
@ -36,27 +37,28 @@ def test_init_default(clirunner, validate_cliresult):
validate_pioproject(os.getcwd())
def test_init_ext_folder(clirunner, validate_cliresult):
with clirunner.isolated_filesystem():
ext_folder_name = "ext_folder"
os.makedirs(ext_folder_name)
result = clirunner.invoke(project_init_cmd, ["-d", ext_folder_name])
validate_cliresult(result)
validate_pioproject(os.path.join(os.getcwd(), ext_folder_name))
def test_init_duplicated_boards(clirunner, validate_cliresult, tmpdir):
with tmpdir.as_cwd():
for _ in range(2):
result = clirunner.invoke(
project_init_cmd,
["-b", "uno", "-b", "uno", "--no-install-dependencies"],
)
validate_cliresult(result)
validate_pioproject(str(tmpdir))
config = ProjectConfig(os.path.join(os.getcwd(), "platformio.ini"))
config.validate()
assert set(config.sections()) == set(["env:uno"])
project_dir = str(tmpdir.join("ext_folder"))
os.makedirs(project_dir)
with fs.cd(os.path.dirname(project_dir)):
result = clirunner.invoke(
project_init_cmd,
[
"-d",
os.path.basename(project_dir),
"-b",
"uno",
"-b",
"uno",
"--no-install-dependencies",
],
)
validate_cliresult(result)
validate_pioproject(project_dir)
config = ProjectConfig(os.path.join(project_dir, "platformio.ini"))
config.validate()
assert set(config.sections()) == set(["env:uno"])
def test_init_ide_without_board(clirunner, tmpdir):

View File

@ -42,7 +42,7 @@ board = devkit
framework = foo
lib_deps =
CustomLib
ArduinoJson @ 5.10.1
ArduinoJson @ 6.18.5
"""
)
result = clirunner.invoke(
@ -163,7 +163,7 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
storage_dir = tmpdir_factory.mktemp("test-updates")
result = clirunner.invoke(
cmd_lib,
["-d", str(storage_dir), "install", "ArduinoJson @ 5.10.1", "Blynk @ ~0.5.0"],
["-d", str(storage_dir), "install", "ArduinoJson @ 6.18.5", "Blynk @ ~1.2"],
)
validate_cliresult(result)
result = clirunner.invoke(
@ -173,17 +173,17 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
outdated = json.loads(result.stdout)
assert len(outdated) == 2
# ArduinoJson
assert outdated[0]["version"] == "5.10.1"
assert outdated[0]["version"] == "6.18.5"
assert outdated[0]["versionWanted"] is None
assert semantic_version.Version(
outdated[0]["versionLatest"]
) > semantic_version.Version("6.16.0")
) > semantic_version.Version("6.18.5")
# Blynk
assert outdated[1]["version"] == "0.5.4"
assert outdated[1]["version"] == "1.2.0"
assert outdated[1]["versionWanted"] is None
assert semantic_version.Version(
outdated[1]["versionLatest"]
) > semantic_version.Version("0.6.0")
) > semantic_version.Version("1.2.0")
# check with spec
result = clirunner.invoke(
@ -194,19 +194,19 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
"update",
"--dry-run",
"--json-output",
"ArduinoJson @ ^5",
"ArduinoJson @ ^6",
],
)
validate_cliresult(result)
outdated = json.loads(result.stdout)
assert outdated[0]["version"] == "5.10.1"
assert outdated[0]["versionWanted"] == "5.13.4"
assert outdated[0]["version"] == "6.18.5"
assert outdated[0]["versionWanted"] == "6.21.5"
assert semantic_version.Version(
outdated[0]["versionLatest"]
) > semantic_version.Version("6.16.0")
# update with spec
result = clirunner.invoke(
cmd_lib, ["-d", str(storage_dir), "update", "--silent", "ArduinoJson @ ^5.10.1"]
cmd_lib, ["-d", str(storage_dir), "update", "--silent", "ArduinoJson @ ^6.18.5"]
)
validate_cliresult(result)
result = clirunner.invoke(
@ -215,12 +215,12 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
validate_cliresult(result)
items = json.loads(result.stdout)
assert len(items) == 2
assert items[0]["version"] == "5.13.4"
assert items[1]["version"] == "0.5.4"
assert items[0]["version"] == "6.21.5"
assert items[1]["version"] == "1.2.0"
# Check incompatible
result = clirunner.invoke(
cmd_lib, ["-d", str(storage_dir), "update", "--dry-run", "ArduinoJson @ ^5"]
cmd_lib, ["-d", str(storage_dir), "update", "--dry-run", "ArduinoJson @ ^6"]
)
with pytest.raises(
AssertionError,
@ -228,7 +228,7 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
):
validate_cliresult(result)
result = clirunner.invoke(
cmd_lib, ["-d", str(storage_dir), "update", "ArduinoJson @ ^5"]
cmd_lib, ["-d", str(storage_dir), "update", "ArduinoJson @ ^6"]
)
validate_cliresult(result)
assert "ArduinoJson@5.13.4 is already up-to-date" in result.stdout
assert "ArduinoJson@6.21.5 is already up-to-date" in result.stdout

View File

@ -23,6 +23,7 @@ from platformio.package.exception import UnknownPackageError
from platformio.util import strip_ansi_codes
PlatformioCLI.leftover_args = ["--json-output"] # hook for click
ARDUINO_JSON_VERSION = "6.21.5"
def test_search(clirunner, validate_cliresult):
@ -44,10 +45,10 @@ def test_global_install_registry(clirunner, validate_cliresult, isolated_pio_cor
"-g",
"install",
"64",
"ArduinoJson@~5.10.0",
"547@2.2.4",
"ArduinoJson@~6",
"547@2.7.3",
"AsyncMqttClient@<=0.8.2",
"Adafruit PN532@1.2.0",
"Adafruit PN532@1.3.2",
],
)
validate_cliresult(result)
@ -60,7 +61,7 @@ def test_global_install_registry(clirunner, validate_cliresult, isolated_pio_cor
items1 = [d.basename for d in isolated_pio_core.join("lib").listdir()]
items2 = [
"ArduinoJson",
"ArduinoJson@5.10.1",
f"ArduinoJson@{ARDUINO_JSON_VERSION}",
"NeoPixelBus",
"AsyncMqttClient",
"ESPAsyncTCP",
@ -79,7 +80,7 @@ def test_global_install_archive(clirunner, validate_cliresult, isolated_pio_core
"install",
"https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip",
"https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip@5.8.2",
"SomeLib=https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.8.1/DallasTemperature-3.8.1.tar.gz",
"SomeLib=https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.11.0/DallasTemperature-3.11.0.tar.gz",
"https://github.com/Pedroalbuquerque/ESP32WebServer/archive/master.zip",
],
)
@ -142,7 +143,7 @@ def test_install_duplicates( # pylint: disable=unused-argument
[
"-g",
"install",
"https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.8.1/DallasTemperature-3.8.1.tar.gz",
"https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.11.0/DallasTemperature-3.11.0.tar.gz",
],
)
validate_cliresult(result)
@ -176,11 +177,11 @@ def test_global_lib_list(clirunner, validate_cliresult):
n in result.output
for n in (
"required: https://github.com/Pedroalbuquerque/ESP32WebServer/archive/master.zip",
"ArduinoJson @ 5.10.1",
f"ArduinoJson @ {ARDUINO_JSON_VERSION}",
"required: git+https://github.com/gioblu/PJON.git#3.0",
"PJON @ 3.0.0+sha.1fb26f",
)
)
), result.output
result = clirunner.invoke(cmd_lib, ["-g", "list", "--json-output"])
assert all(
@ -188,7 +189,7 @@ def test_global_lib_list(clirunner, validate_cliresult):
for n in (
"__pkg_dir",
'"__src_url": "git+https://github.com/gioblu/PJON.git#6.2"',
'"version": "5.10.1"',
f'"version": "{ARDUINO_JSON_VERSION}"',
)
)
items1 = [i["name"] for i in json.loads(result.output)]
@ -218,13 +219,13 @@ def test_global_lib_list(clirunner, validate_cliresult):
]
versions2 = [
"ArduinoJson@5.8.2",
"ArduinoJson@5.10.1",
f"ArduinoJson@{ARDUINO_JSON_VERSION}",
"AsyncMqttClient@0.8.2",
"NeoPixelBus@2.2.4",
"NeoPixelBus@2.7.3",
"PJON@6.2.0+sha.07fe9aa",
"PJON@3.0.0+sha.1fb26fd",
"PubSubClient@2.6.0+sha.bef5814",
"Adafruit PN532@1.2.0",
"Adafruit PN532@1.3.2",
]
assert set(versions1) >= set(versions2)
@ -249,7 +250,7 @@ def test_global_lib_update(clirunner, validate_cliresult):
assert "__pkg_dir" in oudated[0]
result = clirunner.invoke(cmd_lib, ["-g", "update", oudated[0]["__pkg_dir"]])
validate_cliresult(result)
assert "Removing NeoPixelBus @ 2.2.4" in strip_ansi_codes(result.output)
assert "Removing NeoPixelBus @ 2.7.3" in strip_ansi_codes(result.output)
# update all libraries
result = clirunner.invoke(

View File

@ -63,7 +63,7 @@ def test_install_unknown_from_registry(clirunner):
def test_install_core_3_dev_platform(clirunner, validate_cliresult, isolated_pio_core):
result = clirunner.invoke(
cli_platform.platform_install,
["atmelavr@1.2.0", "--skip-default-package"],
["atmelavr@2.2.0", "--skip-default-package"],
)
assert result.exit_code == 0
@ -71,11 +71,11 @@ def test_install_core_3_dev_platform(clirunner, validate_cliresult, isolated_pio
def test_install_known_version(clirunner, validate_cliresult, isolated_pio_core):
result = clirunner.invoke(
cli_platform.platform_install,
["atmelavr@2.0.0", "--skip-default-package", "--with-package", "tool-avrdude"],
["atmelavr@4.2.0", "--skip-default-package", "--with-package", "tool-avrdude"],
)
validate_cliresult(result)
output = strip_ansi_codes(result.output)
assert "atmelavr @ 2.0.0" in output
assert "atmelavr@4.2.0" in output
assert not os.path.isdir(str(isolated_pio_core.join("packages")))
@ -128,14 +128,14 @@ def test_update_raw(clirunner, validate_cliresult, isolated_pio_core):
result = clirunner.invoke(cli_platform.platform_update, ["atmelavr"])
validate_cliresult(result)
output = strip_ansi_codes(result.output)
assert "Removing atmelavr @ 2.0.0" in output
assert "Removing atmelavr @ 4.2.0" in output
assert "Platform Manager: Installing platformio/atmelavr @" in output
assert len(isolated_pio_core.join("packages").listdir()) == 2
def test_uninstall(clirunner, validate_cliresult, isolated_pio_core):
result = clirunner.invoke(
cli_platform.platform_uninstall, ["atmelavr@1.2.0", "atmelavr", "espressif8266"]
cli_platform.platform_uninstall, ["atmelavr@2.2.0", "atmelavr", "espressif8266"]
)
validate_cliresult(result)
assert not isolated_pio_core.join("platforms").listdir()

View File

@ -219,7 +219,7 @@ def test_install_from_registry(isolated_pio_core, tmpdir_factory):
# test conflicted names
lm = LibraryPackageManager(str(tmpdir_factory.mktemp("conflicted-storage")))
lm.set_log_level(logging.ERROR)
lm.install("z3t0/IRremote@2.6.1")
lm.install("z3t0/IRremote")
lm.install("mbed-yuhki50/IRremote")
assert len(lm.get_installed()) == 2
@ -554,14 +554,14 @@ def test_uninstall(isolated_pio_core, tmpdir_factory):
assert not lm.get_installed()
# test uninstall dependencies
assert lm.install("AsyncMqttClient-esphome @ 0.8.4")
assert lm.install("AsyncMqttClient-esphome")
assert len(lm.get_installed()) == 3
assert lm.uninstall("AsyncMqttClient-esphome", skip_dependencies=True)
assert len(lm.get_installed()) == 2
lm = LibraryPackageManager(str(storage_dir))
lm.set_log_level(logging.ERROR)
assert lm.install("AsyncMqttClient-esphome @ 0.8.4")
assert lm.install("AsyncMqttClient-esphome")
assert lm.uninstall("AsyncMqttClient-esphome")
assert not lm.get_installed()
@ -604,23 +604,23 @@ def test_update_with_metadata(isolated_pio_core, tmpdir_factory):
assert str(outdated.current) == "1.8.7"
assert outdated.latest > semantic_version.Version("1.10.0")
pkg = lm.install("ArduinoJson @ 5.10.1")
pkg = lm.install("ArduinoJson @ 6.19.4")
# test latest
outdated = lm.outdated(pkg)
assert str(outdated.current) == "5.10.1"
assert str(outdated.current) == "6.19.4"
assert outdated.wanted is None
assert outdated.latest > outdated.current
assert outdated.latest > semantic_version.Version("5.99.99")
# test wanted
outdated = lm.outdated(pkg, PackageSpec("ArduinoJson@~5"))
assert str(outdated.current) == "5.10.1"
assert str(outdated.wanted) == "5.13.4"
outdated = lm.outdated(pkg, PackageSpec("ArduinoJson@~6"))
assert str(outdated.current) == "6.19.4"
assert str(outdated.wanted) == "6.21.5"
assert outdated.latest > semantic_version.Version("6.16.0")
# update to the wanted 5.x
new_pkg = lm.update("ArduinoJson@^5", PackageSpec("ArduinoJson@^5"))
assert str(new_pkg.metadata.version) == "5.13.4"
# update to the wanted 6.x
new_pkg = lm.update("ArduinoJson@^6", PackageSpec("ArduinoJson@^6"))
assert str(new_pkg.metadata.version) == "6.21.5"
# check that old version is removed
assert len(lm.get_installed()) == 2