Merge branch 'release/v6.1.12'

This commit is contained in:
Ivan Kravets
2024-01-10 16:17:10 +02:00
45 changed files with 441 additions and 209 deletions

View File

@ -8,12 +8,12 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-20.04, windows-latest, macos-latest]
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.6", "3.7", "3.11", "3.12"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: "recursive"
@ -37,7 +37,7 @@ jobs:
tox -e lint
- name: Integration Tests
if: ${{ matrix.python-version == '3.9' }}
if: ${{ matrix.python-version == '3.11' }}
run: |
tox -e testcore

View File

@ -12,14 +12,14 @@ jobs:
environment: production
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: "recursive"
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
python-version: "3.11"
- name: Install dependencies
run: |
@ -35,7 +35,8 @@ jobs:
tox -e testcore
- name: Build Python source tarball
run: python setup.py sdist bdist_wheel
# run: python setup.py sdist bdist_wheel
run: python setup.py sdist
- name: Publish package to PyPI
if: ${{ github.ref == 'refs/heads/master' }}

View File

@ -7,13 +7,13 @@ jobs:
name: Build Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: "recursive"
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.9
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
@ -78,7 +78,7 @@ jobs:
fi
- name: Checkout latest Docs
continue-on-error: true
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: ${{ env.DOCS_REPO }}
path: ${{ env.DOCS_DIR }}

View File

@ -15,14 +15,14 @@ jobs:
PIO_INSTALL_DEVPLATFORM_NAMES: "aceinna_imu,atmelavr,atmelmegaavr,atmelsam,espressif32,espressif8266,nordicnrf52,raspberrypi,ststm32,teensy"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: "recursive"
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
python-version: "3.11"
- name: Install dependencies
run: |

View File

@ -40,20 +40,20 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: "recursive"
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: 3.9
python-version: 3.11
- name: Install PlatformIO
run: pip install -U .
- name: Check out ${{ matrix.project.repository }}
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: "recursive"
repository: ${{ matrix.project.repository }}

View File

@ -17,6 +17,26 @@ 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.12 (2024-01-10)
~~~~~~~~~~~~~~~~~~~
* Added support for Python 3.12
* Introduced the capability to launch the debug server in a separate process (`issue #4722 <https://github.com/platformio/platformio-core/issues/4722>`_)
* Introduced a warning during the verification of MCU maximum RAM usage, signaling when the allocated RAM surpasses 100% (`issue #4791 <https://github.com/platformio/platformio-core/issues/4791>`_)
* Drastically enhanced the speed of project building when operating in verbose mode (`issue #4783 <https://github.com/platformio/platformio-core/issues/4783>`_)
* Upgraded the build engine to the latest version of SCons (4.6.0) to improve build performance, reliability, and compatibility with other tools and systems (`release notes <https://github.com/SCons/scons/releases/tag/4.6.0>`__)
* Enhanced the handling of built-in variables in |PIOCONF| during |INTERPOLATION| (`issue #4695 <https://github.com/platformio/platformio-core/issues/4695>`_)
* Enhanced PIP dependency declarations for improved reliability and extended support to include Python 3.6 (`issue #4819 <https://github.com/platformio/platformio-core/issues/4819>`_)
* Implemented automatic installation of missing dependencies when utilizing a SOCKS proxy (`issue #4822 <https://github.com/platformio/platformio-core/issues/4822>`_)
* Implemented a fail-safe mechanism to terminate a debugging session if an unknown CLI option is passed (`issue #4699 <https://github.com/platformio/platformio-core/issues/4699>`_)
* Rectified an issue where ``${platformio.name}`` erroneously represented ``None`` as the default `project name <https://docs.platformio.org/en/latest/projectconf/sections/platformio/options/generic/name.html>`__ (`issue #4717 <https://github.com/platformio/platformio-core/issues/4717>`_)
* Resolved an issue where the ``COMPILATIONDB_INCLUDE_TOOLCHAIN`` setting was not correctly applying to private libraries (`issue #4762 <https://github.com/platformio/platformio-core/issues/4762>`_)
* Resolved an issue where ``get_systype()`` inaccurately returned the architecture when executed within a Docker container on a 64-bit kernel with a 32-bit userspace (`issue #4777 <https://github.com/platformio/platformio-core/issues/4777>`_)
* Resolved an issue with incorrect handling of the ``check_src_filters`` option when used in multiple environments (`issue #4788 <https://github.com/platformio/platformio-core/issues/4788>`_)
* Resolved an issue where running `pio project metadata <https://docs.platformio.org/en/latest/core/userguide/project/cmd_metadata.html>`__ resulted in duplicated "include" entries (`issue #4723 <https://github.com/platformio/platformio-core/issues/4723>`_)
* Resolved an issue where native debugging failed on the host machine (`issue #4745 <https://github.com/platformio/platformio-core/issues/4745>`_)
* Resolved an issue where custom debug configurations were being inadvertently overwritten in VSCode's ``launch.json`` (`issue #4810 <https://github.com/platformio/platformio-core/issues/4810>`_)
6.1.11 (2023-08-31)
~~~~~~~~~~~~~~~~~~~

2
docs

Submodule docs updated: fb83b09c41...c3657d698d

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
VERSION = (6, 1, 11)
VERSION = (6, 1, 12)
__version__ = ".".join([str(s) for s in VERSION])
__title__ = "platformio"
@ -41,7 +41,7 @@ __pioremote_endpoint__ = "ssl:host=remote.platformio.org:port=4413"
__core_packages__ = {
"contrib-piohome": "~3.4.2",
"contrib-pioremote": "~1.0.0",
"tool-scons": "~4.40502.0",
"tool-scons": "~4.40600.0",
"tool-cppcheck": "~1.21100.0",
"tool-clangtidy": "~1.150005.0",
"tool-pvs-studio": "~7.18.0",
@ -52,22 +52,3 @@ __check_internet_hosts__ = [
"88.198.170.159", # platformio.org
"github.com",
] + __registry_mirror_hosts__
__install_requires__ = [
# Core requirements
"bottle == 0.12.*",
"click >=8.0.4, <=8.2",
"colorama",
"marshmallow == 3.*",
"pyelftools == 0.29",
"pyserial == 3.5.*", # keep in sync "device/monitor/terminal.py"
"requests == 2.*",
"semantic_version == 2.10.*",
"tabulate == 0.*",
] + [
# PIO Home requirements
"ajsonrpc == 1.2.*",
"starlette >=0.19, <0.32",
"uvicorn >=0.16, <0.24",
"wsproto == 1.*",
]

View File

@ -171,3 +171,6 @@ ATTRS{product}=="*CMSIS-DAP*", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID
# Atmel AVR Dragon
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"

View File

@ -126,6 +126,10 @@ def ProcessProgramDeps(env):
# remove specified flags
env.ProcessUnFlags(env.get("BUILD_UNFLAGS"))
env.ProcessCompileDbToolchainOption()
def ProcessCompileDbToolchainOption(env):
if "compiledb" in COMMAND_LINE_TARGETS:
# Resolve absolute path of toolchain
for cmd in ("CC", "CXX", "AS"):
@ -138,6 +142,7 @@ def ProcessProgramDeps(env):
)
if env.get("COMPILATIONDB_INCLUDE_TOOLCHAIN"):
print("Warning! `COMPILATIONDB_INCLUDE_TOOLCHAIN` is scoping")
for scope, includes in env.DumpIntegrationIncludes().items():
if scope in ("toolchain",):
env.Append(CPPPATH=includes)
@ -376,6 +381,7 @@ def generate(env):
env.AddMethod(GetBuildType)
env.AddMethod(BuildProgram)
env.AddMethod(ProcessProgramDeps)
env.AddMethod(ProcessCompileDbToolchainOption)
env.AddMethod(ProcessProjectDeps)
env.AddMethod(ParseFlagsExtended)
env.AddMethod(ProcessFlags)

View File

@ -29,12 +29,7 @@ def IsIntegrationDump(_):
def DumpIntegrationIncludes(env):
result = dict(build=[], compatlib=[], toolchain=[])
result["build"].extend(
[
env.subst("$PROJECT_INCLUDE_DIR"),
env.subst("$PROJECT_SRC_DIR"),
]
)
# `env`(project) CPPPATH
result["build"].extend(
[os.path.abspath(env.subst(item)) for item in env.get("CPPPATH", [])]
)

View File

@ -477,6 +477,7 @@ class LibBuilderBase:
self.is_built = True
self.env.PrependUnique(CPPPATH=self.get_include_dirs())
self.env.ProcessCompileDbToolchainOption()
if self.lib_ldf_mode == "off":
for lb in self.env.GetLibBuilders():
@ -791,7 +792,9 @@ class PlatformIOLibBuilder(LibBuilderBase):
include_dirs.append(os.path.join(self.path, "utility"))
for path in self.env.get("CPPPATH", []):
if path not in self.envorigin.get("CPPPATH", []):
if path not in include_dirs and path not in self.envorigin.get(
"CPPPATH", []
):
include_dirs.append(self.env.subst(path))
return include_dirs

View File

@ -218,12 +218,11 @@ def CheckUploadSize(_, target, source, env):
if int(ARGUMENTS.get("PIOVERBOSE", 0)):
print(output)
# raise error
# if data_max_size and data_size > data_max_size:
# sys.stderr.write(
# "Error: The data size (%d bytes) is greater "
# "than maximum allowed (%s bytes)\n" % (data_size, data_max_size))
# env.Exit(1)
if data_max_size and data_size > data_max_size:
sys.stderr.write(
"Warning! The data size (%d bytes) is greater "
"than maximum allowed (%s bytes)\n" % (data_size, data_max_size)
)
if program_size > program_max_size:
sys.stderr.write(
"Error: The program size (%d bytes) is greater "

View File

@ -108,7 +108,7 @@ def cli(
"+<%s>" % os.path.basename(config.get("platformio", "include_dir")),
]
src_filters = (
env_src_filters = (
src_filters
or pattern
or env_options.get(
@ -120,7 +120,7 @@ def cli(
tool_options = dict(
verbose=verbose,
silent=silent,
src_filters=src_filters,
src_filters=env_src_filters,
flags=flags or env_options.get("check_flags"),
severity=[DefectItem.SEVERITY_LABELS[DefectItem.SEVERITY_HIGH]]
if silent

View File

@ -18,9 +18,10 @@ import subprocess
import click
from platformio import VERSION, __install_requires__, __version__, app, exception
from platformio import VERSION, __version__, app, exception
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"
@ -37,7 +38,7 @@ DEVELOP_INIT_SCRIPT_URL = (
@click.option("--verbose", "-v", is_flag=True)
def cli(dev, only_dependencies, verbose):
if only_dependencies:
return upgrade_pypi_dependencies(verbose)
return upgrade_pip_dependencies(verbose)
update_core_packages()
@ -102,7 +103,7 @@ def cli(dev, only_dependencies, verbose):
return True
def upgrade_pypi_dependencies(verbose):
def upgrade_pip_dependencies(verbose):
subprocess.run(
[
get_pythonexe_path(),
@ -111,7 +112,7 @@ def upgrade_pypi_dependencies(verbose):
"install",
"--upgrade",
"pip",
*__install_requires__,
*get_pip_dependencies(),
],
check=True,
stdout=subprocess.PIPE if not verbose else None,

View File

@ -55,7 +55,7 @@ from platformio.project.options import ProjectOptions
@click.option("--load-mode", type=ProjectOptions["env.debug_load_mode"].type)
@click.option("--verbose", "-v", is_flag=True)
@click.option("--interface", type=click.Choice(["gdb"]))
@click.argument("__unprocessed", nargs=-1, type=click.UNPROCESSED)
@click.argument("client_extra_args", nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def cli(
ctx,
@ -65,10 +65,13 @@ def cli(
load_mode,
verbose,
interface,
__unprocessed,
client_extra_args,
):
app.set_session_var("custom_project_conf", project_conf)
if not interface and client_extra_args:
raise click.UsageError("Please specify debugging interface")
# use env variables from Eclipse or CLion
for name in ("CWD", "PWD", "PLATFORMIO_PROJECT_DIR"):
if is_platformio_project(project_dir):
@ -92,7 +95,7 @@ def cli(
env_name,
load_mode,
verbose,
__unprocessed,
client_extra_args,
)
if helpers.is_gdbmi_mode():
os.environ["PLATFORMIO_DISABLE_PROGRESSBAR"] = "true"
@ -103,19 +106,19 @@ def cli(
else:
debug_config = _configure(*configure_args)
_run(project_dir, debug_config, __unprocessed)
_run(project_dir, debug_config, client_extra_args)
return None
def _configure(ctx, project_config, env_name, load_mode, verbose, __unprocessed):
def _configure(ctx, project_config, env_name, load_mode, verbose, client_extra_args):
platform = PlatformFactory.from_env(env_name, autoinstall=True)
debug_config = DebugConfigFactory.new(
platform,
project_config,
env_name,
)
if "--version" in __unprocessed:
if "--version" in client_extra_args:
raise ReturnErrorCode(
subprocess.run(
[debug_config.client_executable_path, "--version"], check=True
@ -161,12 +164,12 @@ def _configure(ctx, project_config, env_name, load_mode, verbose, __unprocessed)
return debug_config
def _run(project_dir, debug_config, __unprocessed):
def _run(project_dir, debug_config, client_extra_args):
loop = asyncio.ProactorEventLoop() if IS_WINDOWS else asyncio.get_event_loop()
asyncio.set_event_loop(loop)
client = GDBClientProcess(project_dir, debug_config)
coro = client.run(__unprocessed)
coro = client.run(client_extra_args)
try:
signal.signal(signal.SIGINT, signal.SIG_IGN)
loop.run_until_complete(coro)

View File

@ -24,7 +24,9 @@ from platformio.project.options import ProjectOptions
class DebugConfigBase: # pylint: disable=too-many-instance-attributes
def __init__(self, platform, project_config, env_name, port=None):
DEFAULT_PORT = None
def __init__(self, platform, project_config, env_name):
self.platform = platform
self.project_config = project_config
self.env_name = env_name
@ -48,7 +50,6 @@ class DebugConfigBase: # pylint: disable=too-many-instance-attributes
self._load_cmds = None
self._port = None
self.port = port
self.server = self._configure_server()
try:
@ -120,8 +121,10 @@ class DebugConfigBase: # pylint: disable=too-many-instance-attributes
@property
def port(self):
return (
self.env_options.get("debug_port", self.tool_settings.get("port"))
or self._port
self._port
or self.env_options.get("debug_port")
or self.tool_settings.get("port")
or self.DEFAULT_PORT
)
@port.setter

View File

@ -27,17 +27,13 @@ class DebugConfigFactory:
@classmethod
def new(cls, platform, project_config, env_name):
board_config = platform.board_config(
project_config.get("env:" + env_name, "board")
)
tool_name = (
board_config.get_debug_tool_name(
project_config.get("env:" + env_name, "debug_tool")
)
if board_config
else None
)
board_id = project_config.get("env:" + env_name, "board")
config_cls = None
tool_name = None
if board_id:
tool_name = platform.board_config(
project_config.get("env:" + env_name, "board")
).get_debug_tool_name(project_config.get("env:" + env_name, "debug_tool"))
try:
mod = importlib.import_module("platformio.debug.config.%s" % tool_name)
config_cls = getattr(mod, cls.get_clsname(tool_name))

View File

@ -16,6 +16,7 @@ from platformio.debug.config.base import DebugConfigBase
class GenericDebugConfig(DebugConfigBase):
DEFAULT_PORT = ":3333"
GDB_INIT_SCRIPT = """
define pio_reset_halt_target
monitor reset halt
@ -31,8 +32,3 @@ $LOAD_CMDS
pio_reset_halt_target
$INIT_BREAK
"""
def __init__(self, *args, **kwargs):
if "port" not in kwargs:
kwargs["port"] = ":3333"
super().__init__(*args, **kwargs)

View File

@ -16,6 +16,7 @@ from platformio.debug.config.base import DebugConfigBase
class JlinkDebugConfig(DebugConfigBase):
DEFAULT_PORT = ":2331"
GDB_INIT_SCRIPT = """
define pio_reset_halt_target
monitor reset
@ -36,11 +37,6 @@ $LOAD_CMDS
$INIT_BREAK
"""
def __init__(self, *args, **kwargs):
if "port" not in kwargs:
kwargs["port"] = ":2331"
super().__init__(*args, **kwargs)
@property
def server_ready_pattern(self):
return super().server_ready_pattern or ("Waiting for GDB connection")

View File

@ -16,6 +16,7 @@ from platformio.debug.config.base import DebugConfigBase
class MspdebugDebugConfig(DebugConfigBase):
DEFAULT_PORT = ":2000"
GDB_INIT_SCRIPT = """
define pio_reset_halt_target
end
@ -29,8 +30,3 @@ $LOAD_CMDS
pio_reset_halt_target
$INIT_BREAK
"""
def __init__(self, *args, **kwargs):
if "port" not in kwargs:
kwargs["port"] = ":2000"
super().__init__(*args, **kwargs)

View File

@ -16,6 +16,7 @@ from platformio.debug.config.base import DebugConfigBase
class QemuDebugConfig(DebugConfigBase):
DEFAULT_PORT = ":1234"
GDB_INIT_SCRIPT = """
define pio_reset_halt_target
monitor system_reset
@ -30,8 +31,3 @@ $LOAD_CMDS
pio_reset_halt_target
$INIT_BREAK
"""
def __init__(self, *args, **kwargs):
if "port" not in kwargs:
kwargs["port"] = ":1234"
super().__init__(*args, **kwargs)

View File

@ -16,6 +16,7 @@ from platformio.debug.config.base import DebugConfigBase
class RenodeDebugConfig(DebugConfigBase):
DEFAULT_PORT = ":3333"
GDB_INIT_SCRIPT = """
define pio_reset_halt_target
monitor machine Reset
@ -33,11 +34,6 @@ $INIT_BREAK
monitor start
"""
def __init__(self, *args, **kwargs):
if "port" not in kwargs:
kwargs["port"] = ":3333"
super().__init__(*args, **kwargs)
@property
def server_ready_pattern(self):
return super().server_ready_pattern or (

View File

@ -62,7 +62,9 @@ class DebugServerProcess(DebugBaseProcess):
openocd_pipe_allowed = all(
[
not self.debug_config.env_options.get("debug_port"),
not self.debug_config.env_options.get(
"debug_port", self.debug_config.tool_settings.get("port")
),
"gdb" in self.debug_config.client_executable_path,
"openocd" in server_executable,
]

View File

@ -210,7 +210,7 @@ def change_filemtime(path, mtime):
def rmtree(path):
def _onerror(func, path, __):
def _onexc(func, path, _):
try:
st_mode = os.stat(path).st_mode
if st_mode & stat.S_IREAD:
@ -223,4 +223,7 @@ def rmtree(path):
err=True,
)
return shutil.rmtree(path, onerror=_onerror)
# pylint: disable=unexpected-keyword-arg, deprecated-argument
if sys.version_info < (3, 12):
return shutil.rmtree(path, onerror=_onexc)
return shutil.rmtree(path, onexc=_onexc)

View File

@ -13,7 +13,6 @@
# limitations under the License.
import json
import os
import socket
from urllib.parse import urljoin
@ -23,6 +22,7 @@ from urllib3.util.retry import Retry
from platformio import __check_internet_hosts__, app, util
from platformio.cache import ContentCache, cleanup_content_cache
from platformio.exception import PlatformioException, UserSideException
from platformio.pipdeps import is_proxy_set
__default_requests_timeout__ = (10, None) # (connect, read)
@ -191,9 +191,7 @@ def _internet_on():
socket.setdefaulttimeout(timeout)
for host in __check_internet_hosts__:
try:
for var in ("HTTP_PROXY", "HTTPS_PROXY"):
if not os.getenv(var) and not os.getenv(var.lower()):
continue
if is_proxy_set():
requests.get("http://%s" % host, allow_redirects=False, timeout=timeout)
return True
# try to resolve `host` for both AF_INET and AF_INET6, and then try to connect

View File

@ -540,6 +540,8 @@ class LibraryPropertiesManifestParser(BaseManifestParser):
"esp32": "espressif32",
"arc32": "intel_arc32",
"stm32": "ststm32",
"nrf52": "nordicnrf52",
"rp2040": "raspberrypi",
}
for arch in properties.get("architectures", "").split(","):
if "particle-" in arch:

View File

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

71
platformio/pipdeps.py Normal file
View File

@ -0,0 +1,71 @@
# 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
import platform
import sys
PY36 = sys.version_info[0:2] == (3, 6)
def get_pip_dependencies():
core = [
"bottle == 0.12.*",
"click >=8.0.4, <9",
"colorama",
"marshmallow == 3.*",
"pyelftools == 0.30",
"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.*",
"tabulate == 0.*",
]
home = [
# PIO Home requirements
"ajsonrpc == 1.2.*",
"starlette >=0.19, <0.35",
"uvicorn %s" % ("== 0.16.0" if PY36 else ">=0.16, <0.26"),
"wsproto == 1.*",
]
extra = []
# issue #4702; Broken "requests/charset_normalizer" on macOS ARM
if platform.system() == "Darwin" and "arm" in platform.machine().lower():
extra.append("chardet>=3.0.2,<6")
# issue 4614: urllib3 v2.0 only supports OpenSSL 1.1.1+
try:
import ssl # pylint: disable=import-outside-toplevel
if ssl.OPENSSL_VERSION.startswith("OpenSSL ") and ssl.OPENSSL_VERSION_INFO < (
1,
1,
1,
):
extra.append("urllib3<2")
except ImportError:
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

@ -169,6 +169,7 @@ class PlatformBase( # pylint: disable=too-many-instance-attributes,too-many-pub
return self._BOARDS_CACHE[id_] if id_ else self._BOARDS_CACHE
def board_config(self, id_):
assert id_
return self.get_boards(id_)
def get_package_type(self, name):

View File

@ -69,7 +69,7 @@ class BuildAsyncPipe(AsyncPipeBase):
print_immediately = False
for char in iter(lambda: self._pipe_reader.read(1), ""):
self._buffer += char
# self._buffer += char
if line and char.strip() and line[-3:] == (char * 3):
print_immediately = True

View File

@ -14,14 +14,16 @@
import configparser
import glob
import hashlib
import json
import os
import re
import time
import click
from platformio import fs
from platformio.compat import MISSING, string_types
from platformio.compat import MISSING, hashlib_encode_data, string_types
from platformio.project import exception
from platformio.project.options import ProjectOptions
@ -41,7 +43,17 @@ CONFIG_HEADER = """
class ProjectConfigBase:
ENVNAME_RE = re.compile(r"^[a-z\d\_\-]+$", flags=re.I)
INLINE_COMMENT_RE = re.compile(r"\s+;.*$")
VARTPL_RE = re.compile(r"\$\{([^\.\}\()]+)\.([^\}]+)\}")
VARTPL_RE = re.compile(r"\$\{(?:([^\.\}\()]+)\.)?([^\}]+)\}")
BUILTIN_VARS = {
"PROJECT_DIR": lambda: os.getcwd(), # pylint: disable=unnecessary-lambda
"PROJECT_HASH": lambda: "%s-%s"
% (
os.path.basename(os.getcwd()),
hashlib.sha1(hashlib_encode_data(os.getcwd())).hexdigest()[:10],
),
"UNIX_TIME": lambda: str(int(time.time())),
}
CUSTOM_OPTION_PREFIXES = ("custom_", "board_")
@ -274,7 +286,7 @@ class ProjectConfigBase:
value = (
default if default != MISSING else self._parser.get(section, option)
)
return self._expand_interpolations(section, value)
return self._expand_interpolations(section, option, value)
if option_meta.sysenvvar:
envvar_value = os.getenv(option_meta.sysenvvar)
@ -297,24 +309,47 @@ class ProjectConfigBase:
if value == MISSING:
return None
return self._expand_interpolations(section, value)
return self._expand_interpolations(section, option, value)
def _expand_interpolations(self, parent_section, value):
if (
not value
or not isinstance(value, string_types)
or not all(["${" in value, "}" in value])
):
def _expand_interpolations(self, section, option, value):
if not value or not isinstance(value, string_types) or not "$" in value:
return value
# legacy support for variables delclared without "${}"
legacy_vars = ["PROJECT_HASH"]
stop = False
while not stop:
stop = True
for name in legacy_vars:
x = value.find(f"${name}")
if x < 0 or value[x - 1] == "$":
continue
value = "%s${%s}%s" % (value[:x], name, value[x + len(name) + 1 :])
stop = False
warn_msg = (
"Invalid variable declaration. Please use "
f"`${{{name}}}` instead of `${name}`"
)
if warn_msg not in self.warnings:
self.warnings.append(warn_msg)
if not all(["${" in value, "}" in value]):
return value
return self.VARTPL_RE.sub(
lambda match: self._re_interpolation_handler(parent_section, match), value
lambda match: self._re_interpolation_handler(section, option, match), value
)
def _re_interpolation_handler(self, parent_section, match):
def _re_interpolation_handler(self, parent_section, parent_option, match):
section, option = match.group(1), match.group(2)
# handle built-in variables
if section is None and option in self.BUILTIN_VARS:
return self.BUILTIN_VARS[option]()
# handle system environment variables
if section == "sysenv":
return os.getenv(option)
# handle ${this.*}
if section == "this":
section = parent_section
@ -322,21 +357,18 @@ class ProjectConfigBase:
if not parent_section.startswith("env:"):
raise exception.ProjectOptionValueError(
f"`${{this.__env__}}` is called from the `{parent_section}` "
"section that is not valid PlatformIO environment, see",
option,
" ",
section,
"section that is not valid PlatformIO environment. Please "
f"check `{parent_option}` option in the `{section}` section"
)
return parent_section[4:]
# handle nested calls
try:
value = self.get(section, option)
except RecursionError as exc:
raise exception.ProjectOptionValueError(
"Infinite recursion has been detected",
option,
" ",
section,
f"Infinite recursion has been detected for `{option}` "
f"option in the `{section}` section"
) from exc
if isinstance(value, list):
return "\n".join(value)
@ -363,10 +395,8 @@ class ProjectConfigBase:
if not self.expand_interpolations:
return value
raise exception.ProjectOptionValueError(
exc.format_message(),
option,
" (%s) " % option_meta.description,
section,
"%s for `%s` option in the `%s` section (%s)"
% (exc.format_message(), option, section, option_meta.description)
)
@staticmethod
@ -439,8 +469,9 @@ class ProjectConfigLintMixin:
try:
config = cls.get_instance(path)
config.validate(silent=True)
warnings = config.warnings
warnings = config.warnings # in case "as_tuple" fails
config.as_tuple()
warnings = config.warnings
except Exception as exc: # pylint: disable=broad-exception-caught
if exc.__cause__ is not None:
exc = exc.__cause__

View File

@ -51,4 +51,4 @@ class InvalidEnvNameError(ProjectError, UserSideException):
class ProjectOptionValueError(ProjectError, UserSideException):
MESSAGE = "{0} for option `{1}`{2}in section [{3}]"
pass

View File

@ -1,4 +1,3 @@
% import codecs
% import json
% import os
%
@ -47,9 +46,14 @@
% return data
% end
%
% def _contains_external_configurations(launch_config):
% def _contains_custom_configurations(launch_config):
% pio_config_names = [
% c["name"]
% for c in get_pio_configurations()
% ]
% return any(
% c.get("type", "") != "platformio-debug"
% or c.get("name", "") in pio_config_names
% for c in launch_config.get("configurations", [])
% )
% end
@ -59,10 +63,14 @@
% return launch_config
% end
%
% pio_config_names = [
% c["name"]
% for c in get_pio_configurations()
% ]
% external_configurations = [
% config
% for config in launch_config["configurations"]
% if config.get("type", "") != "platformio-debug"
% c
% for c in launch_config["configurations"]
% if c.get("type", "") != "platformio-debug" or c.get("name", "") not in pio_config_names
% ]
%
% launch_config["configurations"] = external_configurations
@ -73,11 +81,11 @@
% launch_config = {"version": "0.2.0", "configurations": []}
% launch_file = os.path.join(project_dir, ".vscode", "launch.json")
% if os.path.isfile(launch_file):
% with codecs.open(launch_file, "r", encoding="utf8") as fp:
% with open(launch_file, "r", encoding="utf8") as fp:
% launch_data = _remove_comments(fp.readlines())
% try:
% prev_config = json.loads(launch_data)
% if _contains_external_configurations(prev_config):
% if _contains_custom_configurations(prev_config):
% launch_config = _remove_pio_configurations(prev_config)
% end
% except:
@ -91,9 +99,9 @@
%
// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY
//
// PIO Unified Debugger
// PlatformIO Debugging Solution
//
// Documentation: https://docs.platformio.org/page/plus/debugging.html
// Configuration: https://docs.platformio.org/page/projectconf/section_env_debug.html
// Documentation: https://docs.platformio.org/en/latest/plus/debugging.html
// Configuration: https://docs.platformio.org/en/latest/projectconf/sections/env/options/debug/index.html
{{ json.dumps(get_launch_configuration(), indent=4, ensure_ascii=False) }}

View File

@ -14,14 +14,13 @@
# pylint: disable=redefined-builtin, too-many-arguments
import hashlib
import os
from collections import OrderedDict
import click
from platformio import fs
from platformio.compat import IS_WINDOWS, hashlib_encode_data
from platformio.compat import IS_WINDOWS
class ConfigOption: # pylint: disable=too-many-instance-attributes
@ -80,30 +79,6 @@ def ConfigEnvOption(*args, **kwargs):
return ConfigOption("env", *args, **kwargs)
def calculate_path_hash(path):
return "%s-%s" % (
os.path.basename(path),
hashlib.sha1(hashlib_encode_data(path)).hexdigest()[:10],
)
def expand_dir_templates(path):
project_dir = os.getcwd()
tpls = {
"$PROJECT_DIR": lambda: project_dir,
"$PROJECT_HASH": lambda: calculate_path_hash(project_dir),
}
done = False
while not done:
done = True
for tpl, cb in tpls.items():
if tpl not in path:
continue
path = path.replace(tpl, cb())
done = False
return path
def validate_dir(path):
if not path:
return path
@ -112,8 +87,6 @@ def validate_dir(path):
return path
if path.startswith("~"):
path = fs.expanduser(path)
if "$" in path:
path = expand_dir_templates(path)
return os.path.abspath(path)
@ -137,6 +110,7 @@ ProjectOptions = OrderedDict(
group="generic",
name="name",
description="A project name",
default=lambda: os.path.basename(os.getcwd()),
),
ConfigPlatformioOption(
group="generic",
@ -240,7 +214,7 @@ ProjectOptions = OrderedDict(
"external library dependencies"
),
sysenvvar="PLATFORMIO_WORKSPACE_DIR",
default=os.path.join("$PROJECT_DIR", ".pio"),
default=os.path.join("${PROJECT_DIR}", ".pio"),
validate=validate_dir,
),
ConfigPlatformioOption(
@ -274,7 +248,7 @@ ProjectOptions = OrderedDict(
"System automatically adds this path to CPPPATH scope"
),
sysenvvar="PLATFORMIO_INCLUDE_DIR",
default=os.path.join("$PROJECT_DIR", "include"),
default=os.path.join("${PROJECT_DIR}", "include"),
validate=validate_dir,
),
ConfigPlatformioOption(
@ -285,7 +259,7 @@ ProjectOptions = OrderedDict(
"project C/C++ source files"
),
sysenvvar="PLATFORMIO_SRC_DIR",
default=os.path.join("$PROJECT_DIR", "src"),
default=os.path.join("${PROJECT_DIR}", "src"),
validate=validate_dir,
),
ConfigPlatformioOption(
@ -293,7 +267,7 @@ ProjectOptions = OrderedDict(
name="lib_dir",
description="A storage for the custom/private project libraries",
sysenvvar="PLATFORMIO_LIB_DIR",
default=os.path.join("$PROJECT_DIR", "lib"),
default=os.path.join("${PROJECT_DIR}", "lib"),
validate=validate_dir,
),
ConfigPlatformioOption(
@ -304,7 +278,7 @@ ProjectOptions = OrderedDict(
"file system (SPIFFS, etc.)"
),
sysenvvar="PLATFORMIO_DATA_DIR",
default=os.path.join("$PROJECT_DIR", "data"),
default=os.path.join("${PROJECT_DIR}", "data"),
validate=validate_dir,
),
ConfigPlatformioOption(
@ -315,7 +289,7 @@ ProjectOptions = OrderedDict(
"test source files"
),
sysenvvar="PLATFORMIO_TEST_DIR",
default=os.path.join("$PROJECT_DIR", "test"),
default=os.path.join("${PROJECT_DIR}", "test"),
validate=validate_dir,
),
ConfigPlatformioOption(
@ -323,7 +297,7 @@ ProjectOptions = OrderedDict(
name="boards_dir",
description="A storage for custom board manifests",
sysenvvar="PLATFORMIO_BOARDS_DIR",
default=os.path.join("$PROJECT_DIR", "boards"),
default=os.path.join("${PROJECT_DIR}", "boards"),
validate=validate_dir,
),
ConfigPlatformioOption(
@ -331,7 +305,7 @@ ProjectOptions = OrderedDict(
name="monitor_dir",
description="A storage for custom monitor filters",
sysenvvar="PLATFORMIO_MONITOR_DIR",
default=os.path.join("$PROJECT_DIR", "monitor"),
default=os.path.join("${PROJECT_DIR}", "monitor"),
validate=validate_dir,
),
ConfigPlatformioOption(
@ -342,7 +316,7 @@ ProjectOptions = OrderedDict(
"synchronize extra files between remote machines"
),
sysenvvar="PLATFORMIO_SHARED_DIR",
default=os.path.join("$PROJECT_DIR", "shared"),
default=os.path.join("${PROJECT_DIR}", "shared"),
validate=validate_dir,
),
#

View File

@ -143,6 +143,8 @@ def get_systype():
arch = "x86_" + platform.architecture()[0]
if "x86" in arch:
arch = "amd64" if "64" in arch else "x86"
if arch == "aarch64" and platform.architecture()[0] == "32bit":
arch = "armv7l"
return "%s_%s" % (system, arch) if arch else system
@ -168,9 +170,8 @@ def items_in_list(needle, haystack):
def parse_datetime(datestr):
if "T" in datestr and "Z" in datestr:
return datetime.datetime.strptime(datestr, "%Y-%m-%dT%H:%M:%SZ")
return datetime.datetime.strptime(datestr)
assert "T" in datestr and "Z" in datestr
return datetime.datetime.strptime(datestr, "%Y-%m-%dT%H:%M:%SZ")
def merge_dicts(d1, d2, path=None):

View File

@ -39,7 +39,7 @@ RST_COPYRIGHT = """.. Copyright (c) 2014-present PlatformIO <contact@platformio
limitations under the License.
"""
SKIP_DEBUG_TOOLS = ["esp-bridge", "esp-builtin"]
SKIP_DEBUG_TOOLS = ["esp-bridge", "esp-builtin", "dfu"]
STATIC_FRAMEWORK_DATA = {
"arduino": {

View File

@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import platform
from setuptools import find_packages, setup
from platformio import (
@ -23,13 +22,8 @@ from platformio import (
__title__,
__url__,
__version__,
__install_requires__,
)
# issue #4702; Broken "requests/charset_normalizer" on macOS ARM
if platform.system() == "Darwin" and "arm" in platform.machine().lower():
__install_requires__.append("chardet>=3.0.2,<4")
from platformio.pipdeps import get_pip_dependencies
setup(
name=__title__,
@ -40,7 +34,7 @@ setup(
author_email=__email__,
url=__url__,
license=__license__,
install_requires=__install_requires__,
install_requires=get_pip_dependencies(),
python_requires=">=3.6",
packages=find_packages(include=["platformio", "platformio.*"]),
package_data={

View File

@ -445,7 +445,7 @@ def test_custom_project_libraries(
)
assert pkgs_to_specs(lm.get_installed()) == [
PackageSpec("ArduinoJson@5.13.4"),
PackageSpec("Nanopb@0.4.7"),
PackageSpec("Nanopb@0.4.8"),
]
assert config.get("env:devkit", "lib_deps") == [
"bblanchon/ArduinoJson@^5",

View File

@ -540,6 +540,16 @@ int main() {
"""
)
if framework == "zephyr":
zephyr_dir = tmpdir.mkdir("zephyr")
zephyr_dir.join("prj.conf").write("# nothing here")
zephyr_dir.join("CMakeLists.txt").write(
"""cmake_minimum_required(VERSION 3.16.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(hello_world)
target_sources(app PRIVATE ../src/main.c)"""
)
tmpdir.join("platformio.ini").write(config)
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
validate_cliresult(result)
@ -757,3 +767,39 @@ check_patterns =
assert errors + warnings + style == EXPECTED_DEFECTS * 2
assert "main.cpp" not in result.output
def test_check_src_filter_multiple_envs(clirunner, validate_cliresult, tmpdir_factory):
tmpdir = tmpdir_factory.mktemp("project")
config = """
[env]
check_tool = cppcheck
check_src_filters =
+<src/*>
[env:check_sources]
platform = native
[env:check_tests]
platform = native
check_src_filters =
+<test/*>
"""
tmpdir.join("platformio.ini").write(config)
src_dir = tmpdir.mkdir("src")
src_dir.join("main.cpp").write(TEST_CODE)
src_dir.mkdir("spi").join("spi.cpp").write(TEST_CODE)
tmpdir.mkdir("test").join("test.cpp").write(TEST_CODE)
result = clirunner.invoke(
cmd_check, ["--project-dir", str(tmpdir), "-e", "check_tests"]
)
validate_cliresult(result)
errors, warnings, style = count_defects(result.output)
assert errors + warnings + style == EXPECTED_DEFECTS
assert "test.cpp" in result.output
assert "main.cpp" not in result.output

View File

@ -651,4 +651,4 @@ def test_update_without_metadata(isolated_pio_core, tmpdir_factory):
lm.set_log_level(logging.ERROR)
new_pkg = lm.update(pkg)
assert len(lm.get_installed()) == 4
assert new_pkg.metadata.spec.owner == "ottowinter"
assert new_pkg.metadata.spec.owner == "heman"

View File

@ -33,7 +33,6 @@ BASE_CONFIG = """
[platformio]
env_default = base, extra_2
src_dir = ${custom.src_dir}
build_dir = ${custom.build_dir}
extra_configs =
extra_envs.ini
extra_debug.ini
@ -61,7 +60,6 @@ build_flags = -D RELEASE
[custom]
src_dir = source
build_dir = ~/tmp/pio-$PROJECT_HASH
debug_flags = -D RELEASE
lib_flags = -lc -lm
extra_flags = ${sysenv.__PIO_TEST_CNF_EXTRA_FLAGS}
@ -319,7 +317,6 @@ def test_getraw_value(config):
config.getraw("custom", "debug_server")
== f"\n{packages_dir}/tool-openocd/openocd\n--help"
)
assert config.getraw("platformio", "build_dir") == "~/tmp/pio-$PROJECT_HASH"
# renamed option
assert config.getraw("env:extra_1", "lib_install") == "574"
@ -360,7 +357,6 @@ def test_get_value(config):
assert config.get("platformio", "src_dir") == os.path.abspath(
os.path.join(os.getcwd(), "source")
)
assert "$PROJECT_HASH" not in config.get("platformio", "build_dir")
# renamed option
assert config.get("env:extra_1", "lib_install") == ["574"]
@ -371,7 +367,6 @@ def test_get_value(config):
def test_items(config):
assert config.items("custom") == [
("src_dir", "source"),
("build_dir", "~/tmp/pio-$PROJECT_HASH"),
("debug_flags", "-D DEBUG=1"),
("lib_flags", "-lc -lm"),
("extra_flags", ""),
@ -525,7 +520,6 @@ def test_dump(tmpdir_factory):
[
("env_default", ["base", "extra_2"]),
("src_dir", "${custom.src_dir}"),
("build_dir", "${custom.build_dir}"),
("extra_configs", ["extra_envs.ini", "extra_debug.ini"]),
],
),
@ -549,7 +543,6 @@ def test_dump(tmpdir_factory):
"custom",
[
("src_dir", "source"),
("build_dir", "~/tmp/pio-$PROJECT_HASH"),
("debug_flags", "-D RELEASE"),
("lib_flags", "-lc -lm"),
("extra_flags", "${sysenv.__PIO_TEST_CNF_EXTRA_FLAGS}"),
@ -631,14 +624,40 @@ custom_option = ${this.board}
assert config.get("env:myenv", "build_flags") == ["-Dmyenv"]
def test_project_name(tmp_path: Path):
project_dir = tmp_path / "my-project-name"
project_dir.mkdir()
project_conf = project_dir / "platformio.ini"
project_conf.write_text(
"""
[env:myenv]
"""
)
with fs.cd(str(project_dir)):
config = ProjectConfig(str(project_conf))
assert config.get("platformio", "name") == "my-project-name"
# custom name
project_conf.write_text(
"""
[platformio]
name = custom-project-name
"""
)
config = ProjectConfig(str(project_conf))
assert config.get("platformio", "name") == "custom-project-name"
def test_nested_interpolation(tmp_path: Path):
project_conf = tmp_path / "platformio.ini"
project_conf.write_text(
"""
[platformio]
build_dir = ~/tmp/pio-$PROJECT_HASH
build_dir = /tmp/pio-$PROJECT_HASH
data_dir = $PROJECT_DIR/assets
[env:myenv]
build_flags = -D UTIME=${UNIX_TIME}
test_testing_command =
${platformio.packages_dir}/tool-simavr/bin/simavr
-m
@ -649,6 +668,10 @@ test_testing_command =
"""
)
config = ProjectConfig(str(project_conf))
assert config.get("platformio", "data_dir").endswith(
os.path.join("$PROJECT_DIR", "assets")
)
assert config.get("env:myenv", "build_flags")[0][-10:].isdigit()
testing_command = config.get("env:myenv", "test_testing_command")
assert "$" not in " ".join(testing_command)
@ -707,11 +730,16 @@ def test_linting_warnings(tmp_path: Path):
project_conf = tmp_path / "platformio.ini"
project_conf.write_text(
"""
[platformio]
build_dir = /tmp/pio-$PROJECT_HASH
[env:app1]
lib_use = 1
test_testing_command = /usr/bin/flash-tool -p $UPLOAD_PORT -b $UPLOAD_SPEED
"""
)
result = ProjectConfig.lint(str(project_conf))
assert not result["errors"]
assert result["warnings"] and len(result["warnings"]) == 1
assert result["warnings"] and len(result["warnings"]) == 2
assert "deprecated" in result["warnings"][0]
assert "Invalid variable declaration" in result["warnings"][1]

View File

@ -0,0 +1,82 @@
# 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 json
from platformio.project.commands.metadata import project_metadata_cmd
def test_metadata_dump(clirunner, validate_cliresult, tmpdir):
tmpdir.join("platformio.ini").write(
"""
[env:native]
platform = native
"""
)
component_dir = tmpdir.mkdir("lib").mkdir("component")
component_dir.join("library.json").write(
"""
{
"name": "component",
"version": "1.0.0"
}
"""
)
component_dir.mkdir("include").join("component.h").write(
"""
#define I_AM_COMPONENT
void dummy(void);
"""
)
component_dir.mkdir("src").join("component.cpp").write(
"""
#include <component.h>
void dummy(void ) {};
"""
)
tmpdir.mkdir("src").join("main.c").write(
"""
#include <component.h>
#ifndef I_AM_COMPONENT
#error "I_AM_COMPONENT"
#endif
int main() {
}
"""
)
metadata_path = tmpdir.join("metadata.json")
result = clirunner.invoke(
project_metadata_cmd,
[
"--project-dir",
str(tmpdir),
"-e",
"native",
"--json-output",
"--json-output-path",
str(metadata_path),
],
)
validate_cliresult(result)
with open(str(metadata_path), encoding="utf8") as fp:
metadata = json.load(fp)["native"]
assert len(metadata["includes"]["build"]) == 3
assert len(metadata["includes"]["compatlib"]) == 2

View File

@ -55,7 +55,7 @@ commands =
[testenv:docs]
deps =
sphinx
sphinx-rtd-theme==1.2.2
sphinx-rtd-theme==2.0.0
sphinx-notfound-page
sphinx-copybutton
restructuredtext-lint