Merge branch 'release/v6.1.6'

This commit is contained in:
Ivan Kravets
2023-01-23 13:01:56 +02:00
77 changed files with 725 additions and 116 deletions

View File

@ -7,13 +7,8 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
exclude:
- os: macos-latest
python-version: "3.6"
- os: windows-latest
python-version: "3.10"
os: [ubuntu-20.04, windows-latest, macos-latest]
python-version: ["3.6", "3.9", "3.11"]
runs-on: ${{ matrix.os }}
@ -23,7 +18,7 @@ jobs:
submodules: "recursive"
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

View File

@ -17,7 +17,7 @@ jobs:
submodules: "recursive"
- name: Set up Python
uses: actions/setup-python@v3
uses: actions/setup-python@v4
with:
python-version: "3.9"

View File

@ -7,13 +7,13 @@ jobs:
name: Build Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: "recursive"
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: 3.7
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install --upgrade pip
@ -40,7 +40,7 @@ jobs:
- name: Save artifact
if: ${{ github.event_name == 'push' }}
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: docs
path: ./docs.tar.gz
@ -57,7 +57,7 @@ jobs:
if: ${{ github.event_name == 'push' }}
steps:
- name: Download artifact
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: docs
- name: Unpack artifact
@ -78,7 +78,7 @@ jobs:
fi
- name: Checkout latest Docs
continue-on-error: true
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
repository: ${{ env.DOCS_REPO }}
path: ${{ env.DOCS_DIR }}

View File

@ -20,7 +20,7 @@ jobs:
submodules: "recursive"
- name: Set up Python
uses: actions/setup-python@v3
uses: actions/setup-python@v4
with:
python-version: "3.9"

View File

@ -32,7 +32,7 @@ jobs:
repository: "1technophile/OpenMQTTGateway"
folder: "OpenMQTTGateway"
config_dir: "OpenMQTTGateway"
env_name: "esp32-m5atom"
env_name: "esp32-m5atom-lite"
os: [ubuntu-latest, windows-latest, macos-latest]
exclude:
- os: windows-latest
@ -45,7 +45,7 @@ jobs:
submodules: "recursive"
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
uses: actions/setup-python@v4
with:
python-version: 3.9
@ -53,7 +53,7 @@ jobs:
run: pip install -U .
- name: Check out ${{ matrix.project.repository }}
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: "recursive"
repository: ${{ matrix.project.repository }}

View File

@ -2,6 +2,7 @@ Release Notes
=============
.. |PIOCONF| replace:: `"platformio.ini" <https://docs.platformio.org/en/latest/projectconf.html>`__ configuration file
.. |LIBRARYJSON| replace:: `library.json <https://docs.platformio.org/en/latest/manifests/library-json/index.html>`__
.. |LDF| replace:: `LDF <https://docs.platformio.org/en/latest/librarymanager/ldf.html>`__
.. |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>`__
@ -13,6 +14,19 @@ PlatformIO Core 6
**A professional collaborative platform for declarative, safety-critical, and test-driven embedded development.**
6.1.6 (2023-01-23)
~~~~~~~~~~~~~~~~~~
* Added support for Python 3.11
* Added a new `name <https://docs.platformio.org/en/latest/projectconf/sections/platformio/options/generic/description.html>`__ configuration option to customize a project name (`pull #4498 <https://github.com/platformio/platformio-core/pull/4498>`_)
* Made assets (templates, ``99-platformio-udev.rules``) part of Python's module (`issue #4458 <https://github.com/platformio/platformio-core/issues/4458>`_)
* Updated `Clang-Tidy <https://docs.platformio.org/en/latest/plus/check-tools/clang-tidy.html>`__ check tool to v15.0.5 with new diagnostics and bugfixes
* Removed dependency on the "zeroconf" package and install it only when a user lists mDNS devices (issue with zeroconf's LGPL license)
* Show the real error message instead of "Can not remove temporary directory" when |PIOCONF| is broken (`issue #4480 <https://github.com/platformio/platformio-core/issues/4480>`_)
* Fixed an issue with an incorrect test summary when a testcase name includes a colon (`issue #4508 <https://github.com/platformio/platformio-core/issues/4508>`_)
* Fixed an issue when `extends <https://docs.platformio.org/en/latest/projectconf/sections/env/options/advanced/extends.html>`__ did not override options in the right order (`issue #4462 <https://github.com/platformio/platformio-core/issues/4462>`_)
* Fixed an issue when `pio pkg list <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_list.html>`__ and `pio pkg uninstall <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_uninstall.html>`__ commands fail if there are circular dependencies in the |LIBRARYJSON| manifests (`issue #4475 <https://github.com/platformio/platformio-core/issues/4475>`_)
6.1.5 (2022-11-01)
~~~~~~~~~~~~~~~~~~
@ -46,7 +60,7 @@ PlatformIO Core 6
* Export a ``PIO_UNIT_TESTING`` macro to the project source files and dependent libraries in the |UNITTESTING| mode
* Improved detection of Windows architecture (`issue #4353 <https://github.com/platformio/platformio-core/issues/4353>`_)
* Warn about unknown `device monitor filters <https://docs.platformio.org/en/latest/core/userguide/device/cmd_monitor.html#filters>`__ (`issue #4362 <https://github.com/platformio/platformio-core/issues/4362>`_)
* Fixed a regression bug when `libArchive <https://docs.platformio.org/en/latest/manifests/library-json/fields/build/libarchive.html>`__ option declared in the `library.json <https://docs.platformio.org/en/latest/manifests/library-json/index.html>`__ manifest was ignored (`issue #4351 <https://github.com/platformio/platformio-core/issues/4351>`_)
* Fixed a regression bug when `libArchive <https://docs.platformio.org/en/latest/manifests/library-json/fields/build/libarchive.html>`__ option declared in the |LIBRARYJSON| manifest was ignored (`issue #4351 <https://github.com/platformio/platformio-core/issues/4351>`_)
* Fixed an issue when the `pio pkg publish <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_publish.html>`__ command didn't work with Python 3.6 (`issue #4352 <https://github.com/platformio/platformio-core/issues/4352>`_)
6.1.1 (2022-07-11)

View File

@ -1 +0,0 @@
include LICENSE

2
docs

Submodule docs updated: f82e7f4266...95c339a711

View File

@ -14,7 +14,7 @@
import sys
VERSION = (6, 1, 5)
VERSION = (6, 1, 6)
__version__ = ".".join([str(s) for s in VERSION])
__title__ = "platformio"
@ -49,7 +49,7 @@ __core_packages__ = {
"contrib-pysite": "~2.%d%d.0" % (sys.version_info.major, sys.version_info.minor),
"tool-scons": "~4.40400.0",
"tool-cppcheck": "~1.270.0",
"tool-clangtidy": "~1.120001.0",
"tool-clangtidy": "~1.150005.0",
"tool-pvs-studio": "~7.18.0",
}

View File

@ -0,0 +1,487 @@
{
"$id": "https://example.com/library.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "library.json schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 50,
"description": "A name of a library.\nMust be unique in the PlatformIO Registry\nShould be slug style for simplicity, consistency, and compatibility. Example: HelloWorld\nCan contain a-z, digits, and dashes (but not start/end with them)\nConsecutive dashes and [:;/,@<>] chars are not allowed.",
"required": true
},
"version": {
"type": "string",
"maxLength": 20,
"description": "A version of a current library source code. Can contain a-z, digits, dots or dash and should be Semantic Versioning compatible.",
"required": true
},
"description": {
"type": "string",
"maxLength": 255,
"description": "The field helps users to identify and search for your library with a brief description. Describe the hardware devices (sensors, boards and etc.) which are suitable with it.",
"required": true
},
"keywords": {
"anyOf": [
{
"type": "string",
"maxLength": 255
},
{
"type": "array",
"items": {
"type": "string",
"maxLength": 255
}
}
],
"description": "Used for search by keyword. Helps to make your library easier to discover without people needing to know its name.\nThe keyword should be lowercased, can contain a-z, digits and dash (but not start/end with them). A list from the keywords can be specified with separator , or declared as Array.",
"required": true
},
"homepage": {
"type": "string",
"maxLength": 255,
"description": "Home page of a library (if is different from repository url).",
"required": false
},
"repository": {
"type": "object",
"properties": {
"type": {
"enum": [
"git",
"hg",
"svn"
],
"description": "only “git”, “hg” or “svn” are supported"
},
"url": {
"type": "string"
},
"branch": {
"type": "string",
"description": "if is not specified, default branch will be used. This field will be ignored if tag/release exists with the value of version."
}
},
"description": "The repository in which the source code can be found.",
"required": false
},
"authors": {
"anyOf": [
{
"type": "object",
"properties": {
"name": {
"type": "string",
"required": true,
"description": "Full name"
},
"email": {
"type": "string"
},
"url": {
"type": "string",
"description": "An authors contact page"
},
"maintainer": {
"type": "boolean",
"description": "Specify “maintainer” status"
}
}
},
{
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"required": true,
"description": "Full name"
},
"email": {
"type": "string"
},
"url": {
"type": "string",
"description": "An authors contact page"
},
"maintainer": {
"type": "boolean",
"description": "Specify “maintainer” status"
}
}
}
}
],
"description": "An author contact information\nIf authors field is not defined, PlatformIO will try to fetch data from VCS provider (Github, Gitlab, etc) if repository is declared.",
"required": false
},
"license": {
"type": "string",
"description": "A SPDX license ID or SPDX Expression. You can check the full list of SPDX license IDs (see “Identifier” column).",
"required": false
},
"frameworks": {
"anyOf": [
{
"type": "string",
"description": "espidf, freertos, *, etc'"
},
{
"type": "array",
"items": {
"type": "string",
"description": "espidf, freertos, *, etc'"
}
}
],
"description": "A list with compatible frameworks. The available framework names are defined in the Frameworks section.\nIf the library is compatible with the all frameworks, then do not declare this field or you use *",
"required": false
},
"platforms": {
"anyOf": [
{
"type": "string",
"description": "atmelavr, espressif8266, *, etc'"
},
{
"type": "array",
"items": {
"type": "string",
"description": "atmelavr, espressif8266, *, etc'"
}
}
],
"description": "A list with compatible development platforms. The available platform name are defined in Development Platforms section.\nIf the library is compatible with the all platforms, then do not declare this field or use *.\nPlatformIO does not check platforms for compatibility in default mode. See Compatibility Mode for details. If you need a strict checking for compatible platforms for a library, please set libCompatMode to strict.",
"required": false
},
"headers": {
"anyOf": [
{
"type": "string",
"description": "MyLibrary.h"
},
{
"type": "array",
"items": {
"type": "string",
"description": "FooCore.h, FooFeature.h"
}
}
],
"description": "A list of header files that can be included in a project source files using #include <...> directive.",
"required": false
},
"examples": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"base": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"description": "A list of example patterns.",
"required": "false"
},
"dependencies": {
"anyOf": [
{
"type": "object",
"properties": {
"owner": {
"type": "string",
"description": "an owner name (username) from the PlatformIO Registry"
},
"name": {
"type": "string",
"description": "library name"
},
"version": {
"type": "string",
"description": "Version Requirements or Package Specifications"
},
"frameworks": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "project compatible Frameworks"
},
"platforms": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": " project compatible Development Platforms"
}
}
},
{
"type": "array",
"items": {
"type": "object",
"properties": {
"owner": {
"type": "string",
"description": "an owner name (username) from the PlatformIO Registry"
},
"name": {
"type": "string",
"description": "library name"
},
"version": {
"type": "string",
"description": "Version Requirements or Package Specifications"
},
"frameworks": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "project compatible Frameworks"
},
"platforms": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": " project compatible Development Platforms"
}
}
}
}
],
"description": "A list of dependent libraries that will be automatically installed.",
"required": false
},
"export": {
"type": "object",
"properties": {
"include": {
"type": "array",
"items": {
"type": "string"
},
"description": "Export only files that matched declared patterns.\n* - matches everything\n? - matches any single character\n[seq] - matches any character in seq\n[!seq] - matches any character not in seq"
},
"exclude": {
"type": "array",
"items": {
"type": "string"
},
"description": "Exclude the directories and files which match with exclude patterns."
}
},
"description": "This option is useful if you need to exclude extra data (test code, docs, images, PDFs, etc). It allows one to reduce the size of the final archive.\nTo check which files will be included in the final packages, please use pio pkg pack command.",
"required": false
},
"scripts": {
"type": "object",
"properties": {
"postinstall": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "runs a script AFTER the package has been installed.\nRun a custom Python script located in the package “scripts” folder AFTER the package is installed. Please note that you dont need to specify a Python interpreter for Python scripts"
},
"preuninstall": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "runs a script BEFORE the package is removed.\nRun a custom Bash script BEFORE the package is uninstalled. The script is declared as a list of command arguments and is located at the root of a package"
}
},
"description": "Execute custom scripts during the special Package Management CLI life cycle events",
"required": false
},
"build": {
"type": "object",
"properties": {
"flags": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "Extra flags to control preprocessing, compilation, assembly, and linking processes. More details build_flags.\nKeep in mind when operating with the -I flag (directories to be searched for header files). The path should be relative to the root directory where the library.json manifest is located."
},
"unflags": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "Remove base/initial flags which were set by development platform. More details build_unflags."
},
"includeDir": {
"type": "string",
"description": "Custom directory to be searched for header files. A default value is include and means that folder is located at the root of a library.\nThe Library Dependency Finder (LDF) will pick a library automatically only when a project or other dependent libraries include any header file located in includeDir or srcDir.",
"required": false
},
"srcDir": {
"type": "string",
"description": "Custom location of library source code. A default value is src and means that folder is located in the root of a library.",
"required": "false"
},
"srcFilter": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "Specify which source files should be included/excluded from build process. The path in filter should be relative to the srcDir option of a library.\nSee syntax for build_src_filter.\nPlease note that you can generate source filter “on-the-fly” using extraScript",
"required": false
},
"extraScript": {
"type": "string",
"description": "Launch extra script before a build process.",
"required": "false"
},
"libArchive": {
"type": "boolean",
"description": "Create an archive (*.a, static library) from the object files and link it into a firmware (program). This is default behavior of PlatformIO Build System (\"libArchive\": true).\nSetting \"libArchive\": false will instruct PlatformIO Build System to link object files directly (in-line). This could be useful if you need to override weak symbols defined in framework or other libraries.\nYou can disable library archiving globally using lib_archive option in “platformio.ini” (Project Configuration File).",
"required": "false"
},
"libLDFMode": {
"anyOf": [
{
"enum": [
"off"
],
"description": "“Manual mode”, does not process source files of a project and dependencies. Builds only the libraries that are specified in manifests (library.json, module.json) or using lib_deps option."
},
{
"enum": [
"chain"
],
"description": "[DEFAULT] Parses ALL C/C++ source files of the project and follows only by nested includes (#include ..., chain...) from the libraries. It also parses C, CC, CPP files from libraries which have the same name as included header file. Does not evaluate C/C++ Preprocessor conditional syntax."
},
{
"enum": [
"deep"
],
"description": "Parses ALL C/C++ source files of the project and parses ALL C/C++ source files of the each found dependency (recursively). Does not evaluate C/C++ Preprocessor conditional syntax."
},
{
"enum": [
"chain+"
],
"description": "The same behavior as for the chain but evaluates C/C++ Preprocessor conditional syntax."
},
{
"enum": [
"deep+"
],
"description": "The same behavior as for the deep but evaluates C/C++ Preprocessor conditional syntax."
}
],
"description": "Specify Library Dependency Finder Mode. See Dependency Finder Mode for details.",
"required": false
},
"libCompatMode": {
"type": "string",
"description": "Specify Library Compatibility Mode. See Compatibility Mode for details.",
"required": false
},
"builder": {
"anyOf": [
{
"enum": [
"PlatformIOLibBuilder"
],
"description": "Default Builder"
},
{
"enum": [
"ArduinoLibBuilder"
]
},
{
"enum": [
"MbedLibBuilder"
]
}
],
"description": "Override default PlatformIOLibBuilder with another builder.",
"required": false
}
},
"required": false
}
}
}

View File

@ -76,12 +76,15 @@ ATTRS{idVendor}=="28e9", ATTRS{idProduct}=="0189", MODE="0666", ENV{ID_MM_DEVICE
# FireBeetle-ESP32
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7522", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
#Wio Terminal
# Wio Terminal
ATTRS{idVendor}=="2886", ATTRS{idProduct}=="[08]02d", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# Raspberry Pi Pico
ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="[01]*", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# AIR32F103
ATTRS{idVendor}=="0d28", ATTRS{idProduct}=="0204", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
#
# Debuggers

View File

@ -157,11 +157,6 @@ if env.subst("$BUILD_CACHE_DIR"):
os.makedirs(env.subst("$BUILD_CACHE_DIR"))
env.CacheDir("$BUILD_CACHE_DIR")
is_clean_all = "cleanall" in COMMAND_LINE_TARGETS
if env.GetOption("clean") or is_clean_all:
env.PioClean(is_clean_all)
env.Exit(0)
if not int(ARGUMENTS.get("PIOVERBOSE", 0)):
click.echo("Verbose mode can be enabled via `-v, --verbose` option")
@ -185,6 +180,10 @@ env.SConsignFile(
for item in env.GetExtraScripts("pre"):
env.SConscript(item, exports="env")
if env.IsCleanTarget():
env.CleanProject("cleanall" in COMMAND_LINE_TARGETS)
env.Exit(0)
env.SConscript("$BUILD_SCRIPT")
if "UPLOAD_FLAGS" in env:

View File

@ -16,6 +16,7 @@ import os
from SCons.Action import Action # pylint: disable=import-error
from SCons.Script import ARGUMENTS # pylint: disable=import-error
from SCons.Script import COMMAND_LINE_TARGETS # pylint: disable=import-error
from SCons.Script import AlwaysBuild # pylint: disable=import-error
from platformio import compat, fs
@ -27,7 +28,11 @@ def VerboseAction(_, act, actstr):
return Action(act, actstr)
def PioClean(env, clean_all=False):
def IsCleanTarget(env):
return env.GetOption("clean") or ("cleanall" in COMMAND_LINE_TARGETS)
def CleanProject(env, clean_all=False):
def _relpath(path):
if compat.IS_WINDOWS:
prefix = os.getcwd()[:2].lower()
@ -103,7 +108,8 @@ def exists(_):
def generate(env):
env.AddMethod(VerboseAction)
env.AddMethod(PioClean)
env.AddMethod(IsCleanTarget)
env.AddMethod(CleanProject)
env.AddMethod(AddTarget)
env.AddMethod(AddPlatformTarget)
env.AddMethod(AddCustomTarget)

View File

@ -85,10 +85,7 @@ def get_filesystem_encoding():
def get_locale_encoding():
try:
return locale.getdefaultlocale()[1]
except ValueError:
return None
return locale.getpreferredencoding()
def get_object_members(obj, ignore_private=True):

View File

@ -18,8 +18,6 @@ import re
import time
from glob import glob
import zeroconf
from platformio import __version__, exception, proc
from platformio.compat import IS_MACOS, IS_WINDOWS
@ -84,6 +82,16 @@ def list_logical_devices():
def list_mdns_services():
try:
import zeroconf # pylint: disable=import-outside-toplevel
except ImportError:
result = proc.exec_command(
[proc.get_pythonexe_path(), "-m", "pip", "install", "zeroconf"]
)
if result.get("returncode") != 0:
print(result.get("err"))
import zeroconf # pylint: disable=import-outside-toplevel
class mDNSListener:
def __init__(self):
self._zc = zeroconf.Zeroconf(interfaces=zeroconf.InterfaceChoice.All)

View File

@ -50,6 +50,10 @@ def get_source_dir():
return os.path.dirname(curpath)
def get_assets_dir():
return os.path.join(get_source_dir(), "assets")
def load_json(file_path):
try:
with open(file_path, mode="r", encoding="utf8") as f:
@ -99,7 +103,7 @@ def calculate_folder_size(path):
def get_platformio_udev_rules_path():
return os.path.abspath(
os.path.join(get_source_dir(), "..", "scripts", "99-platformio-udev.rules")
os.path.join(get_assets_dir(), "system", "99-platformio-udev.rules")
)

View File

@ -18,6 +18,7 @@ import click
from platformio.home.helpers import is_port_used
from platformio.home.run import run_server
from platformio.package.manager.core import get_core_package_dir
@click.command("home", short_help="GUI to manage PlatformIO")
@ -48,15 +49,17 @@ from platformio.home.run import run_server
),
)
def cli(port, host, no_open, shutdown_timeout, session_id):
# hook for `platformio-node-helpers`
if host == "__do_not_start__":
# download all dependent packages
get_core_package_dir("contrib-piohome")
return
# Ensure PIO Home mimetypes are known
mimetypes.add_type("text/html", ".html")
mimetypes.add_type("text/css", ".css")
mimetypes.add_type("application/javascript", ".js")
# hook for `platformio-node-helpers`
if host == "__do_not_start__":
return
home_url = "http://%s:%d%s" % (
host,
port,

View File

@ -14,27 +14,11 @@
import socket
from starlette.concurrency import run_in_threadpool
from platformio import util
from platformio.compat import IS_WINDOWS
from platformio.http import HTTPSession
from platformio.proc import where_is_program
class AsyncSession(HTTPSession):
async def request( # pylint: disable=signature-differs,invalid-overridden-method
self, *args, **kwargs
):
func = super().request
return await run_in_threadpool(func, *args, **kwargs)
@util.memoized(expire="60s")
def requests_session():
return AsyncSession()
@util.memoized(expire="60s")
def get_core_fullpath():
return where_is_program("platformio" + (".exe" if IS_WINDOWS else ""))

View File

@ -19,17 +19,25 @@ import shutil
from functools import cmp_to_key
import click
from starlette.concurrency import run_in_threadpool
from platformio import fs
from platformio.cache import ContentCache
from platformio.device.list.util import list_logical_devices
from platformio.home import helpers
from platformio.http import ensure_internet_on
from platformio.http import HTTPSession, ensure_internet_on
class HTTPAsyncSession(HTTPSession):
async def request( # pylint: disable=signature-differs,invalid-overridden-method
self, *args, **kwargs
):
func = super().request
return await run_in_threadpool(func, *args, **kwargs)
class OSRPC:
@staticmethod
async def fetch_content(uri, data=None, headers=None, cache_valid=None):
async def fetch_content(url, data=None, headers=None, cache_valid=None):
if not headers:
headers = {
"User-Agent": (
@ -38,7 +46,7 @@ class OSRPC:
"Safari/603.3.8"
)
}
cache_key = ContentCache.key_from_args(uri, data) if cache_valid else None
cache_key = ContentCache.key_from_args(url, data) if cache_valid else None
with ContentCache() as cc:
if cache_key:
result = cc.get(cache_key)
@ -48,11 +56,11 @@ class OSRPC:
# check internet before and resolve issue with 60 seconds timeout
ensure_internet_on(raise_exception=True)
session = helpers.requests_session()
session = HTTPAsyncSession()
if data:
r = await session.post(uri, data=data, headers=headers)
r = await session.post(url, data=data, headers=headers)
else:
r = await session.get(uri, headers=headers)
r = await session.get(url, headers=headers)
r.raise_for_status()
result = r.text

View File

@ -0,0 +1,29 @@
# 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.
from ajsonrpc.core import JSONRPC20DispatchException
from platformio.registry.client import RegistryClient
class RegistryRPC:
@staticmethod
def call_client(method, *args, **kwargs):
try:
client = RegistryClient()
return getattr(client, method)(*args, **kwargs)
except Exception as exc: # pylint: disable=bare-except
raise JSONRPC20DispatchException(
code=4003, message="Registry Call Error", data=str(exc)
) from exc

View File

@ -33,6 +33,7 @@ from platformio.home.rpc.handlers.misc import MiscRPC
from platformio.home.rpc.handlers.os import OSRPC
from platformio.home.rpc.handlers.piocore import PIOCoreRPC
from platformio.home.rpc.handlers.project import ProjectRPC
from platformio.home.rpc.handlers.registry import RegistryRPC
from platformio.home.rpc.server import WebSocketJSONRPCServerFactory
from platformio.package.manager.core import get_core_package_dir
from platformio.proc import force_exit
@ -72,6 +73,7 @@ def run_server(host, port, no_open, shutdown_timeout, home_url):
ws_rpc_factory.add_object_handler(OSRPC(), namespace="os")
ws_rpc_factory.add_object_handler(PIOCoreRPC(), namespace="core")
ws_rpc_factory.add_object_handler(ProjectRPC(), namespace="project")
ws_rpc_factory.add_object_handler(RegistryRPC(), namespace="registry")
path = urlparse(home_url).path
routes = [

View File

@ -84,10 +84,16 @@ def print_dependency_tree(pm, specs=None, filter_specs=None, level=0, verbose=Fa
if not candidates:
return
candidates = sorted(candidates.values(), key=lambda item: item[0].metadata.name)
for index, (pkg, spec) in enumerate(candidates):
if filtered_pkgs and not _pkg_tree_contains(pm, pkg, filtered_pkgs):
continue
dependencies = pm.get_pkg_dependencies(pkg)
printed_pkgs = pm.memcache_get("__printed_pkgs", [])
if printed_pkgs and pkg.path in printed_pkgs:
continue
printed_pkgs.append(pkg.path)
pm.memcache_set("__printed_pkgs", printed_pkgs)
click.echo(
"%s%s %s"
% (
@ -100,6 +106,8 @@ def print_dependency_tree(pm, specs=None, filter_specs=None, level=0, verbose=Fa
),
)
)
dependencies = pm.get_pkg_dependencies(pkg)
if dependencies:
print_dependency_tree(
pm,

View File

@ -72,7 +72,9 @@ class FileDownloader:
def start(self, with_progress=True, silent=False):
label = "Downloading"
file_size = self.get_size()
itercontent = self._http_response.iter_content(chunk_size=io.DEFAULT_BUFFER_SIZE)
itercontent = self._http_response.iter_content(
chunk_size=io.DEFAULT_BUFFER_SIZE
)
try:
with open(self._destination, "wb") as fp:
if file_size == -1 or not with_progress or silent:

View File

@ -35,6 +35,12 @@ class PackageManagerUninstallMixin:
if not pkg or not pkg.metadata:
raise UnknownPackageError(spec)
uninstalled_pkgs = self.memcache_get("__uninstalled_pkgs", [])
if uninstalled_pkgs and pkg.path in uninstalled_pkgs:
return pkg
uninstalled_pkgs.append(pkg.path)
self.memcache_set("__uninstalled_pkgs", uninstalled_pkgs)
self.log.info(
"Removing %s @ %s"
% (click.style(pkg.metadata.name, fg="cyan"), pkg.metadata.version)

View File

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

View File

@ -168,7 +168,7 @@ class ProjectConfigBase:
yield (section, option)
if self._parser.has_option(section, "extends"):
extends_queue.extend(
self.parse_multi_values(self._parser.get(section, "extends"))[::-1]
self.parse_multi_values(self._parser.get(section, "extends"))
)
def options(self, section=None, env=None):
@ -319,7 +319,13 @@ class ProjectConfigBase:
if section == "this":
section = parent_section
if option == "__env__":
assert parent_section.startswith("env:")
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,
)
return parent_section[4:]
# handle nested calls
try:

View File

@ -15,7 +15,6 @@
import codecs
import os
import sys
from pathlib import Path
import bottle
@ -51,12 +50,17 @@ class ProjectGenerator:
return envname
@staticmethod
def get_supported_ides():
def get_ide_tpls_dir():
return os.path.join(fs.get_assets_dir(), "templates", "ide-projects")
@classmethod
def get_supported_ides(cls):
tpls_dir = cls.get_ide_tpls_dir()
return sorted(
[
item.name
for item in (Path(__file__).parent / "tpls").iterdir()
if item.is_dir()
name
for name in os.listdir(tpls_dir)
if os.path.isdir(os.path.join(tpls_dir, name))
]
)
@ -78,7 +82,9 @@ class ProjectGenerator:
tpl_vars = {
"config": self.config,
"systype": util.get_systype(),
"project_name": os.path.basename(self.project_dir),
"project_name": self.config.get(
"platformio", "name", os.path.basename(self.project_dir)
),
"project_dir": self.project_dir,
"original_env_name": self.original_env_name,
"env_name": self.env_name,
@ -132,12 +138,12 @@ class ProjectGenerator:
def get_tpls(self):
tpls = []
tpls_dir = str(Path(__file__).parent / "tpls" / self.ide)
for root, _, files in os.walk(tpls_dir):
ide_tpls_dir = os.path.join(self.get_ide_tpls_dir(), self.ide)
for root, _, files in os.walk(ide_tpls_dir):
for f in files:
if not f.endswith(".tpl"):
continue
_relpath = root.replace(tpls_dir, "")
_relpath = root.replace(ide_tpls_dir, "")
if _relpath.startswith(os.sep):
_relpath = _relpath[1:]
tpls.append((_relpath, os.path.join(root, f)))

View File

@ -133,6 +133,11 @@ ProjectOptions = OrderedDict(
#
# [platformio]
#
ConfigPlatformioOption(
group="generic",
name="name",
description="A project name",
),
ConfigPlatformioOption(
group="generic",
name="description",

View File

@ -14,12 +14,13 @@
# pylint: disable=unused-import
from platformio.device.list.util import list_serial_ports
from platformio.device.list.util import list_logical_devices, list_serial_ports
from platformio.device.monitor.filters.base import DeviceMonitorFilterBase
from platformio.fs import to_unix_path
from platformio.platform.base import PlatformBase
from platformio.project.config import ProjectConfig
from platformio.project.helpers import get_project_watch_lib_dirs, load_build_metadata
from platformio.project.options import get_config_options_schema
from platformio.test.result import TestCase, TestCaseSource, TestStatus
from platformio.test.runners.base import TestRunnerBase
from platformio.test.runners.doctest import DoctestTestCaseParser

View File

@ -24,6 +24,7 @@ from tabulate import tabulate
from platformio import app, exception, fs, util
from platformio.device.monitor.command import device_monitor_cmd
from platformio.project.config import ProjectConfig
from platformio.project.exception import ProjectError
from platformio.project.helpers import find_project_dir_above, load_build_metadata
from platformio.run.helpers import clean_build_dir, handle_legacy_libdeps
from platformio.run.processor import EnvironmentProcessor
@ -115,6 +116,8 @@ def cli(
build_dir = config.get("platformio", "build_dir")
try:
clean_build_dir(build_dir, config)
except ProjectError as exc:
raise exc
except: # pylint: disable=bare-except
click.secho(
"Can not remove temporary directory `%s`. Please remove "

View File

@ -156,9 +156,6 @@ def cli( # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
runner.start(ctx)
print_suite_footer(test_suite)
# Reset custom project config
app.set_session_var("custom_project_conf", None)
stdout_report = TestReportFactory.new("stdout", test_result)
stdout_report.generate(verbose=verbose or list_tests)
@ -171,6 +168,9 @@ def cli( # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
custom_report = TestReportFactory.new(output_format, test_result)
custom_report.generate(output_path=output_path, verbose=True)
# Reset custom project config
app.set_session_var("custom_project_conf", None)
if test_result.is_errored or test_result.get_status_nums(TestStatus.FAILED):
raise exception.ReturnErrorCode(1)

View File

@ -35,6 +35,7 @@ def list_test_names(project_config):
def list_test_suites(project_config, environments, filters, ignores):
result = []
test_dir = project_config.get("platformio", "test_dir")
default_envs = project_config.default_envs()
test_names = list_test_names(project_config)
for env_name in project_config.envs():
@ -58,5 +59,16 @@ def list_test_suites(project_config, environments, filters, ignores):
test_name != "*"
and any(fnmatch(test_name, p) for p in patterns["ignore"]),
]
result.append(TestSuite(env_name, test_name, finished=any(skip_conditions)))
result.append(
TestSuite(
env_name,
test_name,
finished=any(skip_conditions),
test_dir=os.path.abspath(
test_dir
if test_name == "*"
else os.path.join(test_dir, test_name)
),
)
)
return result

View File

@ -59,6 +59,7 @@ class JsonTestReport(TestReportBase):
result = dict(
env_name=test_suite.env_name,
test_name=test_suite.test_name,
test_dir=test_suite.test_dir,
status=test_suite.status.name,
duration=test_suite.duration,
timestamp=datetime.datetime.fromtimestamp(test_suite.timestamp).strftime(

View File

@ -93,9 +93,10 @@ class TestCase:
class TestSuite:
def __init__(self, env_name, test_name, finished=False):
def __init__(self, env_name, test_name, finished=False, test_dir=None):
self.env_name = env_name
self.test_name = test_name
self.test_dir = test_dir
self.timestamp = 0
self.duration = 0
self._cases = []

View File

@ -29,10 +29,11 @@ class UnityTestRunner(TestRunnerBase):
EXTRA_LIB_DEPS = ["throwtheswitch/Unity@^2.5.2"]
# Example:
# Examples:
# test/test_foo.cpp:44:test_function_foo:FAIL: Expected 32 Was 33
# test/group/test_foo/test_main.cpp:5:test::dummy:FAIL: Expression Evaluated To FALSE
TESTCASE_PARSE_RE = re.compile(
r"(?P<source_file>[^:]+):(?P<source_line>\d+):(?P<name>[^:]+):"
r"(?P<source_file>[^:]+):(?P<source_line>\d+):(?P<name>[^\s]+):"
r"(?P<status>PASS|IGNORE|FAIL)(:\s*(?P<message>.+)$)?"
)

View File

@ -476,13 +476,11 @@ Stable
; Latest stable version
[env:latest_stable]
platform = {name}
board = ...
{board}
; Custom stable version
[env:custom_stable]
platform = {name}@x.y.z
board = ...
{board}
Upstream
~~~~~~~~
@ -490,9 +488,11 @@ Upstream
[env:upstream_develop]
platform = {github_url}.git
board = ...
""".format(
name=p.name, title=p.title, github_url=github_url
{board}""".format(
name=p.name,
title=p.title,
github_url=github_url,
board="board = ...\n" if p.is_embedded() else "",
)
)

View File

@ -39,14 +39,13 @@ minimal_requirements = [
"requests==%s" % ("2.27.1" if PY36 else "2.*"),
"semantic_version==2.10.*",
"tabulate==%s" % ("0.8.10" if PY36 else "0.9.*"),
"zeroconf<1",
]
home_requirements = [
"aiofiles==%s" % ("0.8.0" if PY36 else "22.1.*"),
"ajsonrpc==1.*",
"starlette==%s" % ("0.19.1" if PY36 else "0.21.*"),
"uvicorn==%s" % ("0.16.0" if PY36 else "0.19.*"),
"starlette==%s" % ("0.19.1" if PY36 else "0.23.*"),
"uvicorn==%s" % ("0.16.0" if PY36 else "0.20.*"),
"wsproto==%s" % ("1.0.0" if PY36 else "1.2.*"),
]
@ -61,15 +60,14 @@ setup(
license=__license__,
install_requires=minimal_requirements + home_requirements,
python_requires=">=3.6",
packages=find_packages(exclude=["tests.*", "tests"]) + ["scripts"],
packages=find_packages(include=["platformio", "platformio.*"]),
package_data={
"platformio": [
"project/integration/tpls/*/.*.tpl",
"project/integration/tpls/*/*.tpl",
"project/integration/tpls/*/*/*.tpl",
"project/integration/tpls/*/.*/*.tpl",
],
"scripts": ["99-platformio-udev.rules"],
"assets/system/99-platformio-udev.rules",
"assets/templates/ide-projects/*/*.tpl",
"assets/templates/ide-projects/*/.*.tpl", # include hidden files
"assets/templates/ide-projects/*/.*/*.tpl", # include hidden folders
]
},
entry_points={
"console_scripts": [

View File

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

View File

@ -205,9 +205,9 @@ def test_options(config):
assert config.options(env="test_extends") == [
"extends",
"build_flags",
"monitor_speed",
"lib_ldf_mode",
"lib_compat_mode",
"monitor_speed",
"custom_monitor_speed",
"lib_deps",
"lib_ignore",
@ -245,9 +245,9 @@ def test_sysenv_options(config):
assert config.options(env="test_extends") == [
"extends",
"build_flags",
"monitor_speed",
"lib_ldf_mode",
"lib_compat_mode",
"monitor_speed",
"custom_monitor_speed",
"lib_deps",
"lib_ignore",
@ -427,9 +427,9 @@ def test_items(config):
assert config.items(env="test_extends") == [
("extends", ["strict_settings"]),
("build_flags", ["-D RELEASE"]),
("monitor_speed", 115200),
("lib_ldf_mode", "chain+"),
("lib_compat_mode", "strict"),
("monitor_speed", 115200),
("custom_monitor_speed", "115200"),
("lib_deps", ["Lib1", "Lib2"]),
("lib_ignore", ["LibIgnoreCustom"]),
@ -647,3 +647,24 @@ test_testing_command =
config = ProjectConfig(str(project_conf))
testing_command = config.get("env:myenv", "test_testing_command")
assert "$" not in " ".join(testing_command)
def test_extends_order(tmp_path: Path):
project_conf = tmp_path / "platformio.ini"
project_conf.write_text(
"""
[a]
board = test
[b]
upload_tool = two
[c]
upload_tool = three
[env:native]
extends = a, b, c
"""
)
config = ProjectConfig(str(project_conf))
assert config.get("env:native", "upload_tool") == "three"

14
tox.ini
View File

@ -19,8 +19,8 @@ known_third_party=OpenSSL, SCons, jsonrpc, twisted, zope
[pytest]
filterwarnings =
error
# SCons
ignore:.*_ImportRedirect.find_spec()
# Bottle
ignore:.*'cgi' is deprecated and slated for removal
[testenv]
passenv = *
@ -44,19 +44,19 @@ commands =
[testenv:testcore]
commands =
{envpython} --version
py.test -v --basetemp="{envtmpdir}" -k "not skip_ci" tests --ignore tests/test_examples.py
py.test -v --basetemp={envtmpdir} -k "not skip_ci" tests --ignore tests/test_examples.py
[testenv:testexamples]
commands =
{envpython} scripts/install_devplatforms.py
py.test -v --basetemp="{envtmpdir}" tests/test_examples.py
py.test -v --basetemp={envtmpdir} tests/test_examples.py
[testenv:docs]
deps =
sphinx
sphinx_rtd_theme
sphinx-rtd-theme==1.1.1
sphinx-notfound-page
sphinx_copybutton
sphinx-copybutton
restructuredtext-lint
commands =
sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html
@ -64,6 +64,6 @@ commands =
[testenv:docslinkcheck]
deps =
sphinx
sphinx_rtd_theme
sphinx-rtd-theme
commands =
sphinx-build -W -b linkcheck docs docs/_build/html