diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index c7c020a3b5..c82438d24a 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -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 diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 370333a500..8517d7bdf7 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -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' }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9a64d88295..4404ad545f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -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 }} diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 6bb258e04c..2e7345632a 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -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: | diff --git a/.github/workflows/projects.yml b/.github/workflows/projects.yml index 1d37c5a38a..676efaec12 100644 --- a/.github/workflows/projects.yml +++ b/.github/workflows/projects.yml @@ -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 }} diff --git a/HISTORY.rst b/HISTORY.rst index 6a5b2a0b3c..9624b24486 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -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 `_) +* Introduced a warning during the verification of MCU maximum RAM usage, signaling when the allocated RAM surpasses 100% (`issue #4791 `_) +* Drastically enhanced the speed of project building when operating in verbose mode (`issue #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 `__) +* Enhanced the handling of built-in variables in |PIOCONF| during |INTERPOLATION| (`issue #4695 `_) +* Enhanced PIP dependency declarations for improved reliability and extended support to include Python 3.6 (`issue #4819 `_) +* Implemented automatic installation of missing dependencies when utilizing a SOCKS proxy (`issue #4822 `_) +* Implemented a fail-safe mechanism to terminate a debugging session if an unknown CLI option is passed (`issue #4699 `_) +* Rectified an issue where ``${platformio.name}`` erroneously represented ``None`` as the default `project name `__ (`issue #4717 `_) +* Resolved an issue where the ``COMPILATIONDB_INCLUDE_TOOLCHAIN`` setting was not correctly applying to private libraries (`issue #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 `_) +* Resolved an issue with incorrect handling of the ``check_src_filters`` option when used in multiple environments (`issue #4788 `_) +* Resolved an issue where running `pio project metadata `__ resulted in duplicated "include" entries (`issue #4723 `_) +* Resolved an issue where native debugging failed on the host machine (`issue #4745 `_) +* Resolved an issue where custom debug configurations were being inadvertently overwritten in VSCode's ``launch.json`` (`issue #4810 `_) + 6.1.11 (2023-08-31) ~~~~~~~~~~~~~~~~~~~ diff --git a/docs b/docs index fb83b09c41..c3657d698d 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit fb83b09c415516ec37018b10d801ddf5c946d794 +Subproject commit c3657d698d29cb8ba02bc709958ec22e7f5def04 diff --git a/examples b/examples index 28c58d3b7c..f06e9656a4 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit 28c58d3b7c4b88d3b1e0c1d2f7d99119a9c89c39 +Subproject commit f06e9656a490c17b9193ce78dca8df4c7a6cb82e diff --git a/platformio/__init__.py b/platformio/__init__.py index c0226cf359..0e79928a26 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -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 @@ __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 @@ "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.*", -] diff --git a/platformio/assets/system/99-platformio-udev.rules b/platformio/assets/system/99-platformio-udev.rules index a5897f8f10..992676db37 100644 --- a/platformio/assets/system/99-platformio-udev.rules +++ b/platformio/assets/system/99-platformio-udev.rules @@ -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" \ No newline at end of file diff --git a/platformio/builder/tools/piobuild.py b/platformio/builder/tools/piobuild.py index 74725a0206..047090c75c 100644 --- a/platformio/builder/tools/piobuild.py +++ b/platformio/builder/tools/piobuild.py @@ -126,6 +126,10 @@ def _append_pio_macros(): # 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 _append_pio_macros(): ) 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) diff --git a/platformio/builder/tools/piointegration.py b/platformio/builder/tools/piointegration.py index 6f0a972ee1..00ecb88a09 100644 --- a/platformio/builder/tools/piointegration.py +++ b/platformio/builder/tools/piointegration.py @@ -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", [])] ) diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index dee1d5bcef..aa18cd1a01 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -477,6 +477,7 @@ def build(self): 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 @@ def get_include_dirs(self): 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 diff --git a/platformio/builder/tools/pioupload.py b/platformio/builder/tools/pioupload.py index 77bf28d251..224d0b90f8 100644 --- a/platformio/builder/tools/pioupload.py +++ b/platformio/builder/tools/pioupload.py @@ -218,12 +218,11 @@ def _format_availale_bytes(value, total): 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 " diff --git a/platformio/check/cli.py b/platformio/check/cli.py index d99da3137b..1deced8766 100644 --- a/platformio/check/cli.py +++ b/platformio/check/cli.py @@ -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 diff --git a/platformio/commands/upgrade.py b/platformio/commands/upgrade.py index 3fb611eaa6..129e3fa189 100644 --- a/platformio/commands/upgrade.py +++ b/platformio/commands/upgrade.py @@ -18,9 +18,10 @@ 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 @@ @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, diff --git a/platformio/debug/cli.py b/platformio/debug/cli.py index 5c9375aec0..25fb68e1b4 100644 --- a/platformio/debug/cli.py +++ b/platformio/debug/cli.py @@ -55,7 +55,7 @@ @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) diff --git a/platformio/debug/config/base.py b/platformio/debug/config/base.py index cb49658174..212d00fa2b 100644 --- a/platformio/debug/config/base.py +++ b/platformio/debug/config/base.py @@ -24,7 +24,9 @@ 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 @@ def __init__(self, platform, project_config, env_name, port=None): self._load_cmds = None self._port = None - self.port = port self.server = self._configure_server() try: @@ -120,8 +121,10 @@ def extra_cmds(self): @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 diff --git a/platformio/debug/config/factory.py b/platformio/debug/config/factory.py index d7358b920b..ea36b6a09c 100644 --- a/platformio/debug/config/factory.py +++ b/platformio/debug/config/factory.py @@ -27,17 +27,13 @@ def get_clsname(name): @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)) diff --git a/platformio/debug/config/generic.py b/platformio/debug/config/generic.py index faa5a3413e..8f06680216 100644 --- a/platformio/debug/config/generic.py +++ b/platformio/debug/config/generic.py @@ -16,6 +16,7 @@ class GenericDebugConfig(DebugConfigBase): + DEFAULT_PORT = ":3333" GDB_INIT_SCRIPT = """ define pio_reset_halt_target monitor reset halt @@ -31,8 +32,3 @@ class GenericDebugConfig(DebugConfigBase): pio_reset_halt_target $INIT_BREAK """ - - def __init__(self, *args, **kwargs): - if "port" not in kwargs: - kwargs["port"] = ":3333" - super().__init__(*args, **kwargs) diff --git a/platformio/debug/config/jlink.py b/platformio/debug/config/jlink.py index a820e0aa7a..5e4cc9ce58 100644 --- a/platformio/debug/config/jlink.py +++ b/platformio/debug/config/jlink.py @@ -16,6 +16,7 @@ class JlinkDebugConfig(DebugConfigBase): + DEFAULT_PORT = ":2331" GDB_INIT_SCRIPT = """ define pio_reset_halt_target monitor reset @@ -36,11 +37,6 @@ class JlinkDebugConfig(DebugConfigBase): $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") diff --git a/platformio/debug/config/mspdebug.py b/platformio/debug/config/mspdebug.py index 7f4d8e1abb..aac6741657 100644 --- a/platformio/debug/config/mspdebug.py +++ b/platformio/debug/config/mspdebug.py @@ -16,6 +16,7 @@ class MspdebugDebugConfig(DebugConfigBase): + DEFAULT_PORT = ":2000" GDB_INIT_SCRIPT = """ define pio_reset_halt_target end @@ -29,8 +30,3 @@ class MspdebugDebugConfig(DebugConfigBase): pio_reset_halt_target $INIT_BREAK """ - - def __init__(self, *args, **kwargs): - if "port" not in kwargs: - kwargs["port"] = ":2000" - super().__init__(*args, **kwargs) diff --git a/platformio/debug/config/qemu.py b/platformio/debug/config/qemu.py index b998c78217..b28a5f34e4 100644 --- a/platformio/debug/config/qemu.py +++ b/platformio/debug/config/qemu.py @@ -16,6 +16,7 @@ class QemuDebugConfig(DebugConfigBase): + DEFAULT_PORT = ":1234" GDB_INIT_SCRIPT = """ define pio_reset_halt_target monitor system_reset @@ -30,8 +31,3 @@ class QemuDebugConfig(DebugConfigBase): pio_reset_halt_target $INIT_BREAK """ - - def __init__(self, *args, **kwargs): - if "port" not in kwargs: - kwargs["port"] = ":1234" - super().__init__(*args, **kwargs) diff --git a/platformio/debug/config/renode.py b/platformio/debug/config/renode.py index f2f62d66fb..de7bba48b2 100644 --- a/platformio/debug/config/renode.py +++ b/platformio/debug/config/renode.py @@ -16,6 +16,7 @@ class RenodeDebugConfig(DebugConfigBase): + DEFAULT_PORT = ":3333" GDB_INIT_SCRIPT = """ define pio_reset_halt_target monitor machine Reset @@ -33,11 +34,6 @@ class RenodeDebugConfig(DebugConfigBase): 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 ( diff --git a/platformio/debug/process/server.py b/platformio/debug/process/server.py index 693dfa445d..7724ddf7a6 100644 --- a/platformio/debug/process/server.py +++ b/platformio/debug/process/server.py @@ -62,7 +62,9 @@ async def run(self): # pylint: disable=too-many-branches 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, ] diff --git a/platformio/fs.py b/platformio/fs.py index 1f43a7a318..c70a554e75 100644 --- a/platformio/fs.py +++ b/platformio/fs.py @@ -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 _onerror(func, 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) diff --git a/platformio/http.py b/platformio/http.py index ee67b631e9..563f982fb9 100644 --- a/platformio/http.py +++ b/platformio/http.py @@ -13,7 +13,6 @@ # limitations under the License. import json -import os import socket from urllib.parse import urljoin @@ -23,6 +22,7 @@ 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 diff --git a/platformio/package/manifest/parser.py b/platformio/package/manifest/parser.py index a70af7ea77..d3fe6bfa42 100644 --- a/platformio/package/manifest/parser.py +++ b/platformio/package/manifest/parser.py @@ -540,6 +540,8 @@ def _parse_platforms(self, properties): "esp32": "espressif32", "arc32": "intel_arc32", "stm32": "ststm32", + "nrf52": "nordicnrf52", + "rp2040": "raspberrypi", } for arch in properties.get("architectures", "").split(","): if "particle-" in arch: diff --git a/platformio/package/manifest/schema.py b/platformio/package/manifest/schema.py index 95e081080b..eeffd83e7b 100644 --- a/platformio/package/manifest/schema.py +++ b/platformio/package/manifest/schema.py @@ -276,7 +276,7 @@ def validate_license(self, value): @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" diff --git a/platformio/pipdeps.py b/platformio/pipdeps.py new file mode 100644 index 0000000000..e9674dda6b --- /dev/null +++ b/platformio/pipdeps.py @@ -0,0 +1,71 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 diff --git a/platformio/platform/base.py b/platformio/platform/base.py index 51b02216a5..0d2d57a9dc 100644 --- a/platformio/platform/base.py +++ b/platformio/platform/base.py @@ -169,6 +169,7 @@ def _append_board(board_id, manifest_path): 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): diff --git a/platformio/proc.py b/platformio/proc.py index acfce4d942..707245a138 100644 --- a/platformio/proc.py +++ b/platformio/proc.py @@ -69,7 +69,7 @@ def do_reading(self): 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 diff --git a/platformio/project/config.py b/platformio/project/config.py index 8020fdd305..3d92a5aebb 100644 --- a/platformio/project/config.py +++ b/platformio/project/config.py @@ -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 @@ 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 @@ def getraw( 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 @@ def getraw( 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 @@ def _re_interpolation_handler(self, parent_section, match): 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 @@ def get(self, section, option, default=MISSING): 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 @@ def lint(cls, path=None): 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__ diff --git a/platformio/project/exception.py b/platformio/project/exception.py index 3821c865b6..cf97c69ed6 100644 --- a/platformio/project/exception.py +++ b/platformio/project/exception.py @@ -51,4 +51,4 @@ class InvalidEnvNameError(ProjectError, UserSideException): class ProjectOptionValueError(ProjectError, UserSideException): - MESSAGE = "{0} for option `{1}`{2}in section [{3}]" + pass diff --git a/platformio/project/integration/tpls/vscode/.vscode/launch.json.tpl b/platformio/project/integration/tpls/vscode/.vscode/launch.json.tpl index 4bd188d2fd..03906ccd3f 100644 --- a/platformio/project/integration/tpls/vscode/.vscode/launch.json.tpl +++ b/platformio/project/integration/tpls/vscode/.vscode/launch.json.tpl @@ -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) }} diff --git a/platformio/project/options.py b/platformio/project/options.py index 2e13815ac4..51611d2478 100644 --- a/platformio/project/options.py +++ b/platformio/project/options.py @@ -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 @@ def get_default_core_dir(): group="generic", name="name", description="A project name", + default=lambda: os.path.basename(os.getcwd()), ), ConfigPlatformioOption( group="generic", @@ -240,7 +214,7 @@ def get_default_core_dir(): "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 @@ def get_default_core_dir(): "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 @@ def get_default_core_dir(): "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 @@ def get_default_core_dir(): 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 @@ def get_default_core_dir(): "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 @@ def get_default_core_dir(): "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 @@ def get_default_core_dir(): 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 @@ def get_default_core_dir(): 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 @@ def get_default_core_dir(): "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, ), # diff --git a/platformio/util.py b/platformio/util.py index 004ac5c9f1..54db102f55 100644 --- a/platformio/util.py +++ b/platformio/util.py @@ -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): diff --git a/scripts/docspregen.py b/scripts/docspregen.py index a4d56c0382..1b595b3ff5 100644 --- a/scripts/docspregen.py +++ b/scripts/docspregen.py @@ -39,7 +39,7 @@ limitations under the License. """ -SKIP_DEBUG_TOOLS = ["esp-bridge", "esp-builtin"] +SKIP_DEBUG_TOOLS = ["esp-bridge", "esp-builtin", "dfu"] STATIC_FRAMEWORK_DATA = { "arduino": { diff --git a/setup.py b/setup.py index 46716cc507..7a7c507631 100644 --- a/setup.py +++ b/setup.py @@ -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 @@ __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 @@ 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={ diff --git a/tests/commands/pkg/test_install.py b/tests/commands/pkg/test_install.py index edcbc241eb..22963ef911 100644 --- a/tests/commands/pkg/test_install.py +++ b/tests/commands/pkg/test_install.py @@ -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", diff --git a/tests/commands/test_check.py b/tests/commands/test_check.py index 5403ee0fa7..a9a99d6509 100644 --- a/tests/commands/test_check.py +++ b/tests/commands/test_check.py @@ -540,6 +540,16 @@ def test_check_embedded_platform_all_tools( """ ) + 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 @@ def test_check_src_filter_from_config_legacy( 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 = + + + +[env:check_sources] +platform = native + +[env:check_tests] +platform = native +check_src_filters = + + + """ + 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 diff --git a/tests/package/test_manager.py b/tests/package/test_manager.py index c1de90b106..fa3f215c25 100644 --- a/tests/package/test_manager.py +++ b/tests/package/test_manager.py @@ -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" diff --git a/tests/project/test_config.py b/tests/project/test_config.py index 6b74285538..3f368fe14b 100644 --- a/tests/project/test_config.py +++ b/tests/project/test_config.py @@ -33,7 +33,6 @@ [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 @@ [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 @@ def test_this(tmp_path: Path): 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 @@ def test_nested_interpolation(tmp_path: Path): """ ) 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] diff --git a/tests/project/test_metadata.py b/tests/project/test_metadata.py new file mode 100644 index 0000000000..a536dd728a --- /dev/null +++ b/tests/project/test_metadata.py @@ -0,0 +1,82 @@ +# Copyright (c) 2014-present PlatformIO +# +# 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 + +void dummy(void ) {}; + """ + ) + + tmpdir.mkdir("src").join("main.c").write( + """ +#include + +#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 diff --git a/tox.ini b/tox.ini index 2eda9b906c..02b9615277 100644 --- a/tox.ini +++ b/tox.ini @@ -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