Merge branch 'feature/refactor-project-options' into develop

This commit is contained in:
Ivan Kravets
2019-05-30 22:15:16 +03:00
31 changed files with 981 additions and 786 deletions

View File

@ -21,7 +21,7 @@ matrix:
install: install:
- git submodule update --init --recursive - git submodule update --init --recursive
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then curl -fsSL https://bootstrap.pypa.io/get-pip.py | sudo python; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then curl -fsSL https://bootstrap.pypa.io/get-pip.py | sudo python; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo pip install "tox==3.0.0"; else pip install -U tox; fi - pip install -U tox
# ChipKIT issue: install 32-bit support for GCC PIC32 # ChipKIT issue: install 32-bit support for GCC PIC32
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install libc6-i386; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install libc6-i386; fi

15
.vscode/settings.json vendored
View File

@ -1,15 +0,0 @@
{
"python.pythonPath": "${workspaceRoot}/.tox/develop/bin/python",
"python.formatting.provider": "yapf",
"files.exclude": {
"**/*.pyc": true,
"*.egg-info": true,
".cache": true,
"build": true,
"dist": true
},
"editor.rulers": [79],
"restructuredtext.builtDocumentationPath": "${workspaceRoot}/docs/_build/html",
"restructuredtext.confPath": "${workspaceRoot}/docs",
"restructuredtext.linter.executablePath": "${workspaceRoot}/.tox/docs/bin/restructuredtext-lint"
}

View File

@ -10,7 +10,7 @@ yapf:
yapf --recursive --in-place platformio/ yapf --recursive --in-place platformio/
test: test:
py.test -v -s -n 3 --dist=loadscope tests --ignore tests/test_examples.py --ignore tests/test_pkgmanifest.py py.test --verbose --capture=no --exitfirst -n 3 --dist=loadscope tests --ignore tests/test_examples.py --ignore tests/test_pkgmanifest.py
before-commit: isort yapf lint test before-commit: isort yapf lint test

View File

@ -16,7 +16,7 @@ import base64
import json import json
import sys import sys
from os import environ from os import environ
from os.path import expanduser, join from os.path import join
from time import time from time import time
from SCons.Script import ARGUMENTS # pylint: disable=import-error from SCons.Script import ARGUMENTS # pylint: disable=import-error
@ -33,76 +33,29 @@ from platformio import util
from platformio.compat import PY2, path_to_unicode from platformio.compat import PY2, path_to_unicode
from platformio.proc import get_pythonexe_path from platformio.proc import get_pythonexe_path
from platformio.project import helpers as project_helpers from platformio.project import helpers as project_helpers
from platformio.project.config import ProjectConfig
AllowSubstExceptions(NameError) AllowSubstExceptions(NameError)
# allow common variables from INI file # append CLI arguments to build environment
commonvars = Variables(None) clivars = Variables(None)
commonvars.AddVariables( clivars.AddVariables(
("PLATFORM_MANIFEST",), ("PLATFORM_MANIFEST",),
("BUILD_SCRIPT",), ("BUILD_SCRIPT",),
("EXTRA_SCRIPTS",), ("PROJECT_CONFIG",),
("PIOENV",), ("PIOENV",),
("PIOTEST",), ("PIOTEST",),
("PIOPLATFORM",), ("UPLOAD_PORT",)
("PIOFRAMEWORK",),
# build options
("BUILD_FLAGS",),
("SRC_BUILD_FLAGS",),
("BUILD_UNFLAGS",),
("SRC_FILTER",),
# library options
("LIB_LDF_MODE",),
("LIB_COMPAT_MODE",),
("LIB_DEPS",),
("LIB_IGNORE",),
("LIB_EXTRA_DIRS",),
("LIB_ARCHIVE",),
# board options
("BOARD",),
# deprecated options, use board_{object.path} instead
("BOARD_MCU",),
("BOARD_F_CPU",),
("BOARD_F_FLASH",),
("BOARD_FLASH_MODE",),
# end of deprecated options
# upload options
("UPLOAD_PORT",),
("UPLOAD_PROTOCOL",),
("UPLOAD_SPEED",),
("UPLOAD_FLAGS",),
("UPLOAD_RESETMETHOD",),
# test options
("TEST_BUILD_PROJECT_SRC",),
# debug options
("DEBUG_TOOL",),
("DEBUG_SVD_PATH",),
) # yapf: disable ) # yapf: disable
MULTILINE_VARS = [
"EXTRA_SCRIPTS", "PIOFRAMEWORK", "BUILD_FLAGS", "SRC_BUILD_FLAGS",
"BUILD_UNFLAGS", "UPLOAD_FLAGS", "SRC_FILTER", "LIB_DEPS", "LIB_IGNORE",
"LIB_EXTRA_DIRS"
]
DEFAULT_ENV_OPTIONS = dict( DEFAULT_ENV_OPTIONS = dict(
tools=[ tools=[
"ar", "gas", "gcc", "g++", "gnulink", "platformio", "pioplatform", "ar", "gas", "gcc", "g++", "gnulink", "platformio", "pioplatform",
"piowinhooks", "piolib", "pioupload", "piomisc", "pioide" "pioproject", "piowinhooks", "piolib", "pioupload", "piomisc", "pioide"
], # yapf: disable ],
toolpath=[join(util.get_source_dir(), "builder", "tools")], toolpath=[join(util.get_source_dir(), "builder", "tools")],
variables=commonvars, variables=clivars,
# Propagating External Environment # Propagating External Environment
PIOVARIABLES=list(commonvars.keys()),
ENV=environ, ENV=environ,
UNIX_TIME=int(time()), UNIX_TIME=int(time()),
PROJECT_DIR=project_helpers.get_project_dir(), PROJECT_DIR=project_helpers.get_project_dir(),
@ -136,38 +89,22 @@ if not int(ARGUMENTS.get("PIOVERBOSE", 0)):
env = DefaultEnvironment(**DEFAULT_ENV_OPTIONS) env = DefaultEnvironment(**DEFAULT_ENV_OPTIONS)
# decode common variables
for k in list(commonvars.keys()):
if k in env:
env[k] = base64.b64decode(env[k])
if isinstance(env[k], bytes):
env[k] = env[k].decode()
if k in MULTILINE_VARS:
env[k] = ProjectConfig.parse_multi_values(env[k])
if env.GetOption('clean'): if env.GetOption('clean'):
env.PioClean(env.subst("$BUILD_DIR")) env.PioClean(env.subst("$BUILD_DIR"))
env.Exit(0) env.Exit(0)
elif not int(ARGUMENTS.get("PIOVERBOSE", 0)): elif not int(ARGUMENTS.get("PIOVERBOSE", 0)):
print("Verbose mode can be enabled via `-v, --verbose` option") print("Verbose mode can be enabled via `-v, --verbose` option")
# Handle custom variables from system environment # Load variables from CLI
for var in ("BUILD_FLAGS", "SRC_BUILD_FLAGS", "SRC_FILTER", "EXTRA_SCRIPTS", for key in list(clivars.keys()):
"UPLOAD_PORT", "UPLOAD_FLAGS", "LIB_EXTRA_DIRS"): if key in env:
k = "PLATFORMIO_%s" % var env[key] = base64.b64decode(env[key])
if k not in environ: if isinstance(env[key], bytes):
continue env[key] = env[key].decode()
if var in ("UPLOAD_PORT", ):
env[var] = environ.get(k)
continue
env.Append(**{var: ProjectConfig.parse_multi_values(environ.get(k))})
env.Prepend(LIBSOURCE_DIRS=env.get("LIB_EXTRA_DIRS", [])) env.GetProjectConfig().validate([env['PIOENV']], silent=True)
env['LIBSOURCE_DIRS'] = [ env.LoadProjectOptions()
expanduser(d) if d.startswith("~") else d for d in env['LIBSOURCE_DIRS'] env.LoadPioPlatform()
]
env.LoadPioPlatform(commonvars)
env.SConscriptChdir(0) env.SConscriptChdir(0)
env.SConsignFile( env.SConsignFile(

View File

@ -113,7 +113,7 @@ def _dump_defines(env):
def _get_svd_path(env): def _get_svd_path(env):
svd_path = env.subst("$DEBUG_SVD_PATH") svd_path = env.GetProjectOption("debug_svd_path")
if svd_path: if svd_path:
return abspath(svd_path) return abspath(svd_path)
@ -139,8 +139,7 @@ def DumpIDEData(env, projenv):
LINTCXXCOM = "$CXXFLAGS $CCFLAGS $CPPFLAGS" LINTCXXCOM = "$CXXFLAGS $CCFLAGS $CPPFLAGS"
data = { data = {
"libsource_dirs": "libsource_dirs": [env.subst(l) for l in env.GetLibSourceDirs()],
[env.subst(l) for l in env.get("LIBSOURCE_DIRS", [])],
"defines": "defines":
_dump_defines(env), _dump_defines(env),
"includes": "includes":

View File

@ -23,8 +23,8 @@ import os
import re import re
import sys import sys
from glob import glob from glob import glob
from os.path import (basename, commonprefix, dirname, isdir, isfile, join, from os.path import (basename, commonprefix, dirname, expanduser, isdir,
realpath, sep) isfile, join, realpath, sep)
import SCons.Scanner # pylint: disable=import-error import SCons.Scanner # pylint: disable=import-error
from SCons.Script import ARGUMENTS # pylint: disable=import-error from SCons.Script import ARGUMENTS # pylint: disable=import-error
@ -207,17 +207,18 @@ class LibBuilderBase(object):
@property @property
def lib_archive(self): def lib_archive(self):
return self.env.get("LIB_ARCHIVE", "") != "false" return self.env.GetProjectOption("lib_archive", True)
@property @property
def lib_ldf_mode(self): def lib_ldf_mode(self):
return self.validate_ldf_mode( return self.validate_ldf_mode(
self.env.get("LIB_LDF_MODE", self.LDF_MODE_DEFAULT)) self.env.GetProjectOption("lib_ldf_mode", self.LDF_MODE_DEFAULT))
@property @property
def lib_compat_mode(self): def lib_compat_mode(self):
return self.validate_compat_mode( return self.validate_compat_mode(
self.env.get("LIB_COMPAT_MODE", self.COMPAT_MODE_DEFAULT)) self.env.GetProjectOption("lib_compat_mode",
self.COMPAT_MODE_DEFAULT))
@property @property
def depbuilders(self): def depbuilders(self):
@ -867,7 +868,7 @@ class ProjectAsLibBuilder(LibBuilderBase):
pass pass
def process_dependencies(self): # pylint: disable=too-many-branches def process_dependencies(self): # pylint: disable=too-many-branches
uris = self.env.get("LIB_DEPS", []) uris = self.env.GetProjectOption("lib_deps", [])
if not uris: if not uris:
return return
storage_dirs = [] storage_dirs = []
@ -907,6 +908,14 @@ class ProjectAsLibBuilder(LibBuilderBase):
return result return result
def GetLibSourceDirs(env):
items = env.GetProjectOption("lib_extra_dirs", [])
items.extend(env['LIBSOURCE_DIRS'])
return [
expanduser(item) if item.startswith("~") else item for item in items
]
def GetLibBuilders(env): # pylint: disable=too-many-branches def GetLibBuilders(env): # pylint: disable=too-many-branches
if "__PIO_LIB_BUILDERS" in DefaultEnvironment(): if "__PIO_LIB_BUILDERS" in DefaultEnvironment():
@ -920,7 +929,7 @@ def GetLibBuilders(env): # pylint: disable=too-many-branches
def _check_lib_builder(lb): def _check_lib_builder(lb):
compat_mode = lb.lib_compat_mode compat_mode = lb.lib_compat_mode
if lb.name in env.get("LIB_IGNORE", []): if lb.name in env.GetProjectOption("lib_ignore", []):
if verbose: if verbose:
sys.stderr.write("Ignored library %s\n" % lb.path) sys.stderr.write("Ignored library %s\n" % lb.path)
return None return None
@ -939,7 +948,7 @@ def GetLibBuilders(env): # pylint: disable=too-many-branches
return True return True
found_incompat = False found_incompat = False
for libs_dir in env['LIBSOURCE_DIRS']: for libs_dir in env.GetLibSourceDirs():
libs_dir = env.subst(libs_dir) libs_dir = env.subst(libs_dir)
if not isdir(libs_dir): if not isdir(libs_dir):
continue continue
@ -1038,6 +1047,7 @@ def exists(_):
def generate(env): def generate(env):
env.AddMethod(GetLibSourceDirs)
env.AddMethod(GetLibBuilders) env.AddMethod(GetLibBuilders)
env.AddMethod(ConfigureProjectLibBuilder) env.AddMethod(ConfigureProjectLibBuilder)
return env return env

View File

@ -322,7 +322,7 @@ def ProcessTest(env):
def GetExtraScripts(env, scope): def GetExtraScripts(env, scope):
items = [] items = []
for item in env.get("EXTRA_SCRIPTS", []): for item in env.GetProjectOption("extra_scripts", []):
if scope == "post" and ":" not in item: if scope == "post" and ":" not in item:
items.append(item) items.append(item)
elif item.startswith("%s:" % scope): elif item.startswith("%s:" % scope):

View File

@ -14,7 +14,6 @@
from __future__ import absolute_import from __future__ import absolute_import
import base64
import sys import sys
from os.path import isdir, isfile, join from os.path import isdir, isfile, join
@ -23,6 +22,7 @@ from SCons.Script import COMMAND_LINE_TARGETS # pylint: disable=import-error
from platformio import exception, util from platformio import exception, util
from platformio.compat import WINDOWS from platformio.compat import WINDOWS
from platformio.managers.platform import PlatformFactory from platformio.managers.platform import PlatformFactory
from platformio.project.config import ProjectOptions
# pylint: disable=too-many-branches, too-many-locals # pylint: disable=too-many-branches, too-many-locals
@ -33,10 +33,10 @@ def initPioPlatform(name):
def PioPlatform(env): def PioPlatform(env):
variables = {} variables = env.GetProjectOptions(as_dict=True)
for name in env['PIOVARIABLES']: if "framework" in variables:
if name in env: # support PIO Core 3.0 dev/platforms
variables[name.lower()] = env[name] variables['pioframework'] = variables['framework']
p = initPioPlatform(env['PLATFORM_MANIFEST']) p = initPioPlatform(env['PLATFORM_MANIFEST'])
p.configure_default_packages(variables, COMMAND_LINE_TARGETS) p.configure_default_packages(variables, COMMAND_LINE_TARGETS)
return p return p
@ -63,7 +63,7 @@ def GetFrameworkScript(env, framework):
return script_path return script_path
def LoadPioPlatform(env, variables): def LoadPioPlatform(env):
p = env.PioPlatform() p = env.PioPlatform()
installed_packages = p.get_installed_packages() installed_packages = p.get_installed_packages()
@ -92,36 +92,25 @@ def LoadPioPlatform(env, variables):
env.Prepend(LIBPATH=[join(p.get_dir(), "ldscripts")]) env.Prepend(LIBPATH=[join(p.get_dir(), "ldscripts")])
if "BOARD" not in env: if "BOARD" not in env:
# handle _MCU and _F_CPU variables for AVR native
for key, value in variables.UnknownVariables().items():
if not key.startswith("BOARD_"):
continue
value = base64.b64decode(value)
if isinstance(value, bytes):
value = value.decode()
env.Replace(**{key.upper().replace("BUILD.", ""): value})
return return
# update board manifest with a custom data # update board manifest with overridden data from INI config
board_config = env.BoardConfig() board_config = env.BoardConfig()
for key, value in variables.UnknownVariables().items(): for option, value in env.GetProjectOptions():
if not key.startswith("BOARD_"): if option.startswith("board_"):
continue board_config.update(option.lower()[6:], value)
value = base64.b64decode(value)
if isinstance(value, bytes):
value = value.decode()
board_config.update(key.lower()[6:], value)
# update default environment variables # load default variables from board config
for key in list(variables.keys()): for option_meta in ProjectOptions.values():
if key in env or \ if not option_meta.buildenvvar or option_meta.buildenvvar in env:
not any([key.startswith("BOARD_"), key.startswith("UPLOAD_")]):
continue continue
_opt, _val = key.lower().split("_", 1) data_path = (option_meta.name[6:]
if _opt == "board": if option_meta.name.startswith("board_") else
_opt = "build" option_meta.name.replace("_", "."))
if _val in board_config.get(_opt): try:
env.Replace(**{key: board_config.get("%s.%s" % (_opt, _val))}) env[option_meta.buildenvvar] = board_config.get(data_path)
except KeyError:
pass
if "build.ldscript" in board_config: if "build.ldscript" in board_config:
env.Replace(LDSCRIPT_PATH=board_config.get("build.ldscript")) env.Replace(LDSCRIPT_PATH=board_config.get("build.ldscript"))
@ -165,7 +154,7 @@ def PrintConfiguration(env):
data = [ data = [
"CURRENT(%s)" % board_config.get_debug_tool_name( "CURRENT(%s)" % board_config.get_debug_tool_name(
env.subst("$DEBUG_TOOL")) env.GetProjectOption("debug_tool"))
] ]
onboard = [] onboard = []
external = [] external = []

View File

@ -0,0 +1,49 @@
# 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 __future__ import absolute_import
from platformio.project.config import ProjectConfig, ProjectOptions
def GetProjectConfig(env):
return ProjectConfig.get_instance(env['PROJECT_CONFIG'])
def GetProjectOptions(env, as_dict=False):
return env.GetProjectConfig().items(env=env['PIOENV'], as_dict=as_dict)
def GetProjectOption(env, option, default=None):
return env.GetProjectConfig().get("env:" + env['PIOENV'], option, default)
def LoadProjectOptions(env):
for option, value in env.GetProjectOptions():
option_meta = ProjectOptions.get("env." + option)
if not option_meta or not option_meta.buildenvvar:
continue
env[option_meta.buildenvvar] = value
def exists(_):
return True
def generate(env):
env.AddMethod(GetProjectConfig)
env.AddMethod(GetProjectOptions)
env.AddMethod(GetProjectOption)
env.AddMethod(LoadProjectOptions)
return env

View File

@ -163,22 +163,18 @@ def device_list( # pylint: disable=too-many-branches
"--environment", "--environment",
help="Load configuration from `platformio.ini` and specified environment") help="Load configuration from `platformio.ini` and specified environment")
def device_monitor(**kwargs): # pylint: disable=too-many-branches def device_monitor(**kwargs): # pylint: disable=too-many-branches
custom_monitor_flags = [] env_options = {}
try: try:
env_options = get_project_options(kwargs['project_dir'], env_options = get_project_options(kwargs['project_dir'],
kwargs['environment']) kwargs['environment'])
if "monitor_flags" in env_options: for k in ("port", "speed", "rts", "dtr"):
custom_monitor_flags = ProjectConfig.parse_multi_values( k2 = "monitor_%s" % k
env_options['monitor_flags']) if k == "speed":
if env_options: k = "baud"
for k in ("port", "speed", "rts", "dtr"): if kwargs[k] is None and k2 in env_options:
k2 = "monitor_%s" % k kwargs[k] = env_options[k2]
if k == "speed": if k != "port":
k = "baud" kwargs[k] = int(kwargs[k])
if kwargs[k] is None and k2 in env_options:
kwargs[k] = env_options[k2]
if k != "port":
kwargs[k] = int(kwargs[k])
except exception.NotPlatformIOProject: except exception.NotPlatformIOProject:
pass pass
@ -187,12 +183,12 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches
if len(ports) == 1: if len(ports) == 1:
kwargs['port'] = ports[0]['port'] kwargs['port'] = ports[0]['port']
sys.argv = ["monitor"] + custom_monitor_flags sys.argv = ["monitor"] + env_options.get("monitor_flags", [])
for k, v in kwargs.items(): for k, v in kwargs.items():
if k in ("port", "baud", "rts", "dtr", "environment", "project_dir"): if k in ("port", "baud", "rts", "dtr", "environment", "project_dir"):
continue continue
k = "--" + k.replace("_", "-") k = "--" + k.replace("_", "-")
if k in custom_monitor_flags: if k in env_options.get("monitor_flags", []):
continue continue
if isinstance(v, bool): if isinstance(v, bool):
if v: if v:

View File

@ -41,13 +41,11 @@ class ProjectRPC(object):
def _get_project_data(project_dir): def _get_project_data(project_dir):
data = {"boards": [], "envLibdepsDirs": [], "libExtraDirs": []} data = {"boards": [], "envLibdepsDirs": [], "libExtraDirs": []}
config = ProjectConfig(join(project_dir, "platformio.ini")) config = ProjectConfig(join(project_dir, "platformio.ini"))
config.validate(validate_options=False) config.validate(silent=True)
libdeps_dir = get_project_libdeps_dir() libdeps_dir = get_project_libdeps_dir()
if config.has_section("platformio") and \ data['libExtraDirs'].extend(
config.has_option("platformio", "lib_extra_dirs"): config.get("platformio", "lib_extra_dirs", []))
data['libExtraDirs'].extend(
config.getlist("platformio", "lib_extra_dirs"))
for section in config.sections(): for section in config.sections():
if not section.startswith("env:"): if not section.startswith("env:"):
@ -55,9 +53,8 @@ class ProjectRPC(object):
data['envLibdepsDirs'].append(join(libdeps_dir, section[4:])) data['envLibdepsDirs'].append(join(libdeps_dir, section[4:]))
if config.has_option(section, "board"): if config.has_option(section, "board"):
data['boards'].append(config.get(section, "board")) data['boards'].append(config.get(section, "board"))
if config.has_option(section, "lib_extra_dirs"): data['libExtraDirs'].extend(
data['libExtraDirs'].extend( config.get(section, "lib_extra_dirs", []))
config.getlist(section, "lib_extra_dirs"))
# skip non existing folders and resolve full path # skip non existing folders and resolve full path
for key in ("envLibdepsDirs", "libExtraDirs"): for key in ("envLibdepsDirs", "libExtraDirs"):
@ -232,11 +229,9 @@ class ProjectRPC(object):
project_description = None project_description = None
try: try:
config = ProjectConfig(join(project_dir, "platformio.ini")) config = ProjectConfig(join(project_dir, "platformio.ini"))
config.validate(validate_options=False) config.validate(silent=True)
if config.has_section("platformio") and \ project_description = config.get("platformio",
config.has_option("platformio", "description"): "description")
project_description = config.get(
"platformio", "description")
except exception.PlatformIOProjectException: except exception.PlatformIOProjectException:
continue continue

View File

@ -70,7 +70,6 @@ def cli(
project_option, project_option,
env_prefix, env_prefix,
silent): silent):
if not silent: if not silent:
if project_dir == getcwd(): if project_dir == getcwd():
click.secho( click.secho(

View File

@ -109,9 +109,8 @@ def cli(ctx, **options):
continue continue
storage_dir = join(libdeps_dir, env) storage_dir = join(libdeps_dir, env)
ctx.meta[CTX_META_STORAGE_DIRS_KEY].append(storage_dir) ctx.meta[CTX_META_STORAGE_DIRS_KEY].append(storage_dir)
if config.has_option("env:" + env, "lib_deps"): ctx.meta[CTX_META_STORAGE_LIBDEPS_KEY][storage_dir] = config.get(
ctx.meta[CTX_META_STORAGE_LIBDEPS_KEY][ "env:" + env, "lib_deps", [])
storage_dir] = config.getlist("env:" + env, "lib_deps")
@cli.command("install", short_help="Install library") @cli.command("install", short_help="Install library")
@ -175,8 +174,7 @@ def lib_install( # pylint: disable=too-many-arguments
if project_environments and env not in project_environments: if project_environments and env not in project_environments:
continue continue
config.expand_interpolations = False config.expand_interpolations = False
lib_deps = (config.getlist("env:" + env, "lib_deps") lib_deps = config.get("env:" + env, "lib_deps", [])
if config.has_option("env:" + env, "lib_deps") else [])
for library in libraries: for library in libraries:
if library in lib_deps: if library in lib_deps:
continue continue

View File

@ -1,352 +0,0 @@
# 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 os import getcwd, makedirs
from os.path import getmtime, isdir, isfile, join
from time import time
import click
from platformio import exception, telemetry, util
from platformio.commands.device import device_monitor as cmd_device_monitor
from platformio.commands.lib import (CTX_META_STORAGE_DIRS_KEY,
CTX_META_STORAGE_LIBDEPS_KEY)
from platformio.commands.lib import lib_install as cmd_lib_install
from platformio.commands.platform import \
platform_install as cmd_platform_install
from platformio.managers.platform import PlatformFactory
from platformio.project.config import ProjectConfig
from platformio.project.helpers import (
calculate_project_hash, find_project_dir_above, get_project_build_dir,
get_project_dir, get_project_libdeps_dir)
# pylint: disable=too-many-arguments,too-many-locals,too-many-branches
@click.command("run", short_help="Process project environments")
@click.option("-e", "--environment", multiple=True)
@click.option("-t", "--target", multiple=True)
@click.option("--upload-port")
@click.option(
"-d",
"--project-dir",
default=getcwd,
type=click.Path(
exists=True,
file_okay=True,
dir_okay=True,
writable=True,
resolve_path=True))
@click.option(
"-c",
"--project-conf",
type=click.Path(
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
resolve_path=True))
@click.option("-s", "--silent", is_flag=True)
@click.option("-v", "--verbose", is_flag=True)
@click.option("--disable-auto-clean", is_flag=True)
@click.pass_context
def cli(ctx, environment, target, upload_port, project_dir, project_conf,
silent, verbose, disable_auto_clean):
# find project directory on upper level
if isfile(project_dir):
project_dir = find_project_dir_above(project_dir)
with util.cd(project_dir):
# clean obsolete build dir
if not disable_auto_clean:
try:
_clean_build_dir(get_project_build_dir())
except: # pylint: disable=bare-except
click.secho(
"Can not remove temporary directory `%s`. Please remove "
"it manually to avoid build issues" %
get_project_build_dir(force=True),
fg="yellow")
config = ProjectConfig.get_instance(
project_conf or join(project_dir, "platformio.ini"))
config.validate(environment)
_handle_legacy_libdeps(project_dir, config)
results = []
start_time = time()
default_envs = config.default_envs()
for envname in config.envs():
skipenv = any([
environment and envname not in environment, not environment
and default_envs and envname not in default_envs
])
if skipenv:
results.append((envname, None))
continue
if not silent and any(
status is not None for (_, status) in results):
click.echo()
options = config.items(env=envname, as_dict=True)
if "piotest" not in options and "piotest" in ctx.meta:
options['piotest'] = ctx.meta['piotest']
ep = EnvironmentProcessor(ctx, envname, options, target,
upload_port, silent, verbose)
result = (envname, ep.process())
results.append(result)
if result[1] and "monitor" in ep.get_build_targets() and \
"nobuild" not in ep.get_build_targets():
ctx.invoke(
cmd_device_monitor,
environment=environment[0] if environment else None)
found_error = any(status is False for (_, status) in results)
if (found_error or not silent) and len(results) > 1:
click.echo()
print_summary(results, start_time)
if found_error:
raise exception.ReturnErrorCode(1)
return True
class EnvironmentProcessor(object):
DEFAULT_DUMP_OPTIONS = ("platform", "framework", "board")
IGNORE_BUILD_OPTIONS = [
"test_transport", "test_filter", "test_ignore", "test_port",
"test_speed", "debug_port", "debug_init_cmds", "debug_extra_cmds",
"debug_server", "debug_init_break", "debug_load_cmd",
"debug_load_mode", "monitor_port", "monitor_speed", "monitor_rts",
"monitor_dtr"
]
REMAPED_OPTIONS = {"framework": "pioframework", "platform": "pioplatform"}
def __init__(
self, # pylint: disable=R0913
cmd_ctx,
name,
options,
targets,
upload_port,
silent,
verbose):
self.cmd_ctx = cmd_ctx
self.name = name
self.options = options
self.targets = targets
self.upload_port = upload_port
self.silent = silent
self.verbose = verbose
def process(self):
terminal_width, _ = click.get_terminal_size()
start_time = time()
env_dump = []
for k, v in self.options.items():
self.options[k] = self.options[k].strip()
if self.verbose or k in self.DEFAULT_DUMP_OPTIONS:
env_dump.append("%s: %s" % (k, ", ".join(
ProjectConfig.parse_multi_values(v))))
if not self.silent:
click.echo("Processing %s (%s)" % (click.style(
self.name, fg="cyan", bold=True), "; ".join(env_dump)))
click.secho("-" * terminal_width, bold=True)
result = self._run()
is_error = result['returncode'] != 0
if self.silent and not is_error:
return True
if is_error or "piotest_processor" not in self.cmd_ctx.meta:
print_header(
"[%s] Took %.2f seconds" % (
(click.style("ERROR", fg="red", bold=True) if is_error else
click.style("SUCCESS", fg="green", bold=True)),
time() - start_time),
is_error=is_error)
return not is_error
def get_build_variables(self):
variables = {"pioenv": self.name}
if self.upload_port:
variables['upload_port'] = self.upload_port
for k, v in self.options.items():
if k in self.REMAPED_OPTIONS:
k = self.REMAPED_OPTIONS[k]
if k in self.IGNORE_BUILD_OPTIONS:
continue
if k == "targets" or (k == "upload_port" and self.upload_port):
continue
variables[k] = v
return variables
def get_build_targets(self):
targets = []
if self.targets:
targets = [t for t in self.targets]
elif "targets" in self.options:
targets = self.options['targets'].split(", ")
return targets
def _run(self):
if "platform" not in self.options:
raise exception.UndefinedEnvPlatform(self.name)
build_vars = self.get_build_variables()
build_targets = self.get_build_targets()
telemetry.on_run_environment(self.options, build_targets)
# skip monitor target, we call it above
if "monitor" in build_targets:
build_targets.remove("monitor")
if "nobuild" not in build_targets:
# install dependent libraries
if "lib_install" in self.options:
_autoinstall_libdeps(self.cmd_ctx, self.name, [
int(d.strip())
for d in self.options['lib_install'].split(",")
if d.strip()
], self.verbose)
if "lib_deps" in self.options:
_autoinstall_libdeps(
self.cmd_ctx, self.name,
ProjectConfig.parse_multi_values(self.options['lib_deps']),
self.verbose)
try:
p = PlatformFactory.newPlatform(self.options['platform'])
except exception.UnknownPlatform:
self.cmd_ctx.invoke(
cmd_platform_install,
platforms=[self.options['platform']],
skip_default_package=True)
p = PlatformFactory.newPlatform(self.options['platform'])
return p.run(build_vars, build_targets, self.silent, self.verbose)
def _handle_legacy_libdeps(project_dir, config):
legacy_libdeps_dir = join(project_dir, ".piolibdeps")
if (not isdir(legacy_libdeps_dir)
or legacy_libdeps_dir == get_project_libdeps_dir()):
return
if not config.has_section("env"):
config.add_section("env")
lib_extra_dirs = []
if config.has_option("env", "lib_extra_dirs"):
lib_extra_dirs = config.getlist("env", "lib_extra_dirs")
lib_extra_dirs.append(legacy_libdeps_dir)
config.set("env", "lib_extra_dirs", lib_extra_dirs)
click.secho(
"DEPRECATED! A legacy library storage `{0}` has been found in a "
"project. \nPlease declare project dependencies in `platformio.ini`"
" file using `lib_deps` option and remove `{0}` folder."
"\nMore details -> http://docs.platformio.org/page/projectconf/"
"section_env_library.html#lib-deps".format(legacy_libdeps_dir),
fg="yellow")
def _autoinstall_libdeps(ctx, envname, libraries, verbose=False):
if not libraries:
return
libdeps_dir = join(get_project_libdeps_dir(), envname)
ctx.meta.update({
CTX_META_STORAGE_DIRS_KEY: [libdeps_dir],
CTX_META_STORAGE_LIBDEPS_KEY: {
libdeps_dir: libraries
}
})
try:
ctx.invoke(cmd_lib_install, silent=not verbose)
except exception.InternetIsOffline as e:
click.secho(str(e), fg="yellow")
def _clean_build_dir(build_dir):
# remove legacy ".pioenvs" folder
legacy_build_dir = join(get_project_dir(), ".pioenvs")
if isdir(legacy_build_dir) and legacy_build_dir != build_dir:
util.rmtree_(legacy_build_dir)
structhash_file = join(build_dir, "structure.hash")
proj_hash = calculate_project_hash()
# if project's config is modified
if (isdir(build_dir) and getmtime(
join(get_project_dir(), "platformio.ini")) > getmtime(build_dir)):
util.rmtree_(build_dir)
# check project structure
if isdir(build_dir) and isfile(structhash_file):
with open(structhash_file) as f:
if f.read() == proj_hash:
return
util.rmtree_(build_dir)
if not isdir(build_dir):
makedirs(build_dir)
with open(structhash_file, "w") as f:
f.write(proj_hash)
def print_header(label, is_error=False, fg=None):
terminal_width, _ = click.get_terminal_size()
width = len(click.unstyle(label))
half_line = "=" * int((terminal_width - width - 2) / 2)
click.secho(
"%s %s %s" % (half_line, label, half_line), fg=fg, err=is_error)
def print_summary(results, start_time):
print_header("[%s]" % click.style("SUMMARY"))
envname_max_len = 0
for (envname, _) in results:
if len(envname) > envname_max_len:
envname_max_len = len(envname)
successed = True
for (envname, status) in results:
status_str = click.style("SUCCESS", fg="green")
if status is False:
successed = False
status_str = click.style("ERROR", fg="red")
elif status is None:
status_str = click.style("SKIP", fg="yellow")
format_str = (
"Environment {0:<" + str(envname_max_len + 9) + "}\t[{1}]")
click.echo(
format_str.format(click.style(envname, fg="cyan"), status_str),
err=status is False)
print_header(
"[%s] Took %.2f seconds" % (
(click.style("SUCCESS", fg="green", bold=True) if successed else
click.style("ERROR", fg="red", bold=True)), time() - start_time),
is_error=not successed)

View File

@ -0,0 +1,16 @@
# 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 platformio.commands.run.command import cli
from platformio.commands.run.helpers import print_header

View File

@ -0,0 +1,119 @@
# 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 os import getcwd
from os.path import isfile, join
from time import time
import click
from platformio import exception, util
from platformio.commands.device import device_monitor as cmd_device_monitor
from platformio.commands.run.helpers import (
_clean_build_dir, _handle_legacy_libdeps, print_summary)
from platformio.commands.run.processor import EnvironmentProcessor
from platformio.project.config import ProjectConfig
from platformio.project.helpers import (find_project_dir_above,
get_project_build_dir)
# pylint: disable=too-many-arguments,too-many-locals,too-many-branches
@click.command("run", short_help="Process project environments")
@click.option("-e", "--environment", multiple=True)
@click.option("-t", "--target", multiple=True)
@click.option("--upload-port")
@click.option(
"-d",
"--project-dir",
default=getcwd,
type=click.Path(
exists=True,
file_okay=True,
dir_okay=True,
writable=True,
resolve_path=True))
@click.option(
"-c",
"--project-conf",
type=click.Path(
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
resolve_path=True))
@click.option("-s", "--silent", is_flag=True)
@click.option("-v", "--verbose", is_flag=True)
@click.option("--disable-auto-clean", is_flag=True)
@click.pass_context
def cli(ctx, environment, target, upload_port, project_dir, project_conf,
silent, verbose, disable_auto_clean):
# find project directory on upper level
if isfile(project_dir):
project_dir = find_project_dir_above(project_dir)
with util.cd(project_dir):
# clean obsolete build dir
if not disable_auto_clean:
try:
_clean_build_dir(get_project_build_dir())
except: # pylint: disable=bare-except
click.secho(
"Can not remove temporary directory `%s`. Please remove "
"it manually to avoid build issues" %
get_project_build_dir(force=True),
fg="yellow")
config = ProjectConfig.get_instance(
project_conf or join(project_dir, "platformio.ini"))
config.validate(environment)
_handle_legacy_libdeps(project_dir, config)
results = []
start_time = time()
default_envs = config.default_envs()
for envname in config.envs():
skipenv = any([
environment and envname not in environment, not environment
and default_envs and envname not in default_envs
])
if skipenv:
results.append((envname, None))
continue
if not silent and any(
status is not None for (_, status) in results):
click.echo()
ep = EnvironmentProcessor(ctx, envname, config, target,
upload_port, silent, verbose)
result = (envname, ep.process())
results.append(result)
if result[1] and "monitor" in ep.get_build_targets() and \
"nobuild" not in ep.get_build_targets():
ctx.invoke(
cmd_device_monitor,
environment=environment[0] if environment else None)
found_error = any(status is False for (_, status) in results)
if (found_error or not silent) and len(results) > 1:
click.echo()
print_summary(results, start_time)
if found_error:
raise exception.ReturnErrorCode(1)
return True

View File

@ -0,0 +1,127 @@
# 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 os import makedirs
from os.path import getmtime, isdir, isfile, join
from time import time
import click
from platformio import exception, util
from platformio.commands.lib import (CTX_META_STORAGE_DIRS_KEY,
CTX_META_STORAGE_LIBDEPS_KEY)
from platformio.commands.lib import lib_install as cmd_lib_install
from platformio.project.helpers import (
calculate_project_hash, get_project_dir, get_project_libdeps_dir)
def _handle_legacy_libdeps(project_dir, config):
legacy_libdeps_dir = join(project_dir, ".piolibdeps")
if (not isdir(legacy_libdeps_dir)
or legacy_libdeps_dir == get_project_libdeps_dir()):
return
if not config.has_section("env"):
config.add_section("env")
lib_extra_dirs = config.get("env", "lib_extra_dirs", [])
lib_extra_dirs.append(legacy_libdeps_dir)
config.set("env", "lib_extra_dirs", lib_extra_dirs)
click.secho(
"DEPRECATED! A legacy library storage `{0}` has been found in a "
"project. \nPlease declare project dependencies in `platformio.ini`"
" file using `lib_deps` option and remove `{0}` folder."
"\nMore details -> http://docs.platformio.org/page/projectconf/"
"section_env_library.html#lib-deps".format(legacy_libdeps_dir),
fg="yellow")
def _autoinstall_libdeps(ctx, envname, libraries, verbose=False):
if not libraries:
return
libdeps_dir = join(get_project_libdeps_dir(), envname)
ctx.meta.update({
CTX_META_STORAGE_DIRS_KEY: [libdeps_dir],
CTX_META_STORAGE_LIBDEPS_KEY: {
libdeps_dir: libraries
}
})
try:
ctx.invoke(cmd_lib_install, silent=not verbose)
except exception.InternetIsOffline as e:
click.secho(str(e), fg="yellow")
def _clean_build_dir(build_dir):
# remove legacy ".pioenvs" folder
legacy_build_dir = join(get_project_dir(), ".pioenvs")
if isdir(legacy_build_dir) and legacy_build_dir != build_dir:
util.rmtree_(legacy_build_dir)
structhash_file = join(build_dir, "structure.hash")
proj_hash = calculate_project_hash()
# if project's config is modified
if (isdir(build_dir) and getmtime(
join(get_project_dir(), "platformio.ini")) > getmtime(build_dir)):
util.rmtree_(build_dir)
# check project structure
if isdir(build_dir) and isfile(structhash_file):
with open(structhash_file) as f:
if f.read() == proj_hash:
return
util.rmtree_(build_dir)
if not isdir(build_dir):
makedirs(build_dir)
with open(structhash_file, "w") as f:
f.write(proj_hash)
def print_header(label, is_error=False, fg=None):
terminal_width, _ = click.get_terminal_size()
width = len(click.unstyle(label))
half_line = "=" * int((terminal_width - width - 2) / 2)
click.secho(
"%s %s %s" % (half_line, label, half_line), fg=fg, err=is_error)
def print_summary(results, start_time):
print_header("[%s]" % click.style("SUMMARY"))
succeeded_nums = 0
failed_nums = 0
envname_max_len = max(
[len(click.style(envname, fg="cyan")) for (envname, _) in results])
for (envname, status) in results:
if status is False:
failed_nums += 1
status_str = click.style("FAILED", fg="red")
elif status is None:
status_str = click.style("IGNORED", fg="yellow")
else:
succeeded_nums += 1
status_str = click.style("SUCCESS", fg="green")
format_str = "Environment {0:<%d}\t[{1}]" % envname_max_len
click.echo(
format_str.format(click.style(envname, fg="cyan"), status_str),
err=status is False)
print_header(
"%s%d succeeded in %.2f seconds" %
("%d failed, " % failed_nums if failed_nums else "", succeeded_nums,
time() - start_time),
is_error=failed_nums,
fg="red" if failed_nums else "green")

View File

@ -0,0 +1,115 @@
# 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 time import time
import click
from platformio import exception, telemetry
from platformio.commands.platform import \
platform_install as cmd_platform_install
from platformio.commands.run.helpers import _autoinstall_libdeps, print_header
from platformio.managers.platform import PlatformFactory
# pylint: disable=too-many-instance-attributes
class EnvironmentProcessor(object):
DEFAULT_PRINT_OPTIONS = ("platform", "framework", "board")
def __init__( # pylint: disable=too-many-arguments
self, cmd_ctx, name, config, targets, upload_port, silent,
verbose):
self.cmd_ctx = cmd_ctx
self.name = name
self.config = config
self.targets = targets
self.upload_port = upload_port
self.silent = silent
self.verbose = verbose
self.options = config.items(env=name, as_dict=True)
def process(self):
terminal_width, _ = click.get_terminal_size()
start_time = time()
env_dump = []
for k, v in self.options.items():
if self.verbose or k in self.DEFAULT_PRINT_OPTIONS:
env_dump.append(
"%s: %s" % (k, ", ".join(v) if isinstance(v, list) else v))
if not self.silent:
click.echo("Processing %s (%s)" % (click.style(
self.name, fg="cyan", bold=True), "; ".join(env_dump)))
click.secho("-" * terminal_width, bold=True)
result = self._run_platform()
is_error = result['returncode'] != 0
if self.silent and not is_error:
return True
if is_error or "piotest_processor" not in self.cmd_ctx.meta:
print_header(
"[%s] Took %.2f seconds" % (
(click.style("ERROR", fg="red", bold=True) if is_error else
click.style("SUCCESS", fg="green", bold=True)),
time() - start_time),
is_error=is_error)
return not is_error
def get_build_variables(self):
variables = {"pioenv": self.name, "project_config": self.config.path}
if "piotest" in self.cmd_ctx.meta:
variables['piotest'] = self.cmd_ctx.meta['piotest']
if self.upload_port:
# override upload port with a custom from CLI
variables['upload_port'] = self.upload_port
return variables
def get_build_targets(self):
if self.targets:
return [t for t in self.targets]
return self.config.get("env:" + self.name, "targets", [])
def _run_platform(self):
if "platform" not in self.options:
raise exception.UndefinedEnvPlatform(self.name)
build_vars = self.get_build_variables()
build_targets = self.get_build_targets()
telemetry.on_run_environment(self.options, build_targets)
# skip monitor target, we call it above
if "monitor" in build_targets:
build_targets.remove("monitor")
if "nobuild" not in build_targets and "lib_deps" in self.options:
_autoinstall_libdeps(
self.cmd_ctx, self.name,
self.config.get("env:" + self.name, "lib_deps"), self.verbose)
try:
p = PlatformFactory.newPlatform(self.options['platform'])
except exception.UnknownPlatform:
self.cmd_ctx.invoke(
cmd_platform_install,
platforms=[self.options['platform']],
skip_default_package=True)
p = PlatformFactory.newPlatform(self.options['platform'])
return p.run(build_vars, build_targets, self.silent, self.verbose)

View File

@ -22,7 +22,7 @@ from time import time
import click import click
from platformio import exception, util from platformio import exception, util
from platformio.commands.run import print_header from platformio.commands.run.helpers import print_header
from platformio.commands.test.embedded import EmbeddedTestProcessor from platformio.commands.test.embedded import EmbeddedTestProcessor
from platformio.commands.test.native import NativeTestProcessor from platformio.commands.test.native import NativeTestProcessor
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
@ -107,9 +107,8 @@ def cli( # pylint: disable=redefined-builtin
# filter and ignore patterns # filter and ignore patterns
patterns = dict(filter=list(filter), ignore=list(ignore)) patterns = dict(filter=list(filter), ignore=list(ignore))
for key in patterns: for key in patterns:
if config.has_option(section, "test_%s" % key): patterns[key].extend(
patterns[key].extend( config.get(section, "test_%s" % key, []))
config.getlist(section, "test_%s" % key))
skip_conditions = [ skip_conditions = [
environment and envname not in environment, environment and envname not in environment,
@ -167,10 +166,11 @@ def cli( # pylint: disable=redefined-builtin
passed_nums += 1 passed_nums += 1
status_str = click.style("PASSED", fg="green") status_str = click.style("PASSED", fg="green")
format_str = "test/{:<%d} > {:<%d}\t[{}]" % (testname_max_len,
envname_max_len)
click.echo( click.echo(
("test/{:<%d} > {:<%d}\t[{}]" % format_str.format(testname, click.style(envname, fg="cyan"),
(testname_max_len, envname_max_len)).format( status_str),
testname, click.style(envname, fg="cyan"), status_str),
err=status is False) err=status is False)
print_header( print_header(

View File

@ -32,7 +32,8 @@ class EmbeddedTestProcessor(TestProcessorBase):
target = ["__test"] target = ["__test"]
if self.options['without_uploading']: if self.options['without_uploading']:
target.append("checkprogsize") target.append("checkprogsize")
self.build_or_upload(target) if not self.build_or_upload(target):
return False
if not self.options['without_uploading']: if not self.options['without_uploading']:
self.print_progress("Uploading... (2/3)") self.print_progress("Uploading... (2/3)")
@ -41,7 +42,8 @@ class EmbeddedTestProcessor(TestProcessorBase):
target.append("nobuild") target.append("nobuild")
else: else:
target.append("__test") target.append("__test")
self.build_or_upload(target) if not self.build_or_upload(target):
return False
if self.options['without_testing']: if self.options['without_testing']:
return None return None

View File

@ -25,7 +25,8 @@ class NativeTestProcessor(TestProcessorBase):
def process(self): def process(self):
if not self.options['without_building']: if not self.options['without_building']:
self.print_progress("Building... (1/2)") self.print_progress("Building... (1/2)")
self.build_or_upload(["__test"]) if not self.build_or_upload(["__test"]):
return False
if self.options['without_testing']: if self.options['without_testing']:
return None return None
self.print_progress("Testing... (2/2)") self.print_progress("Testing... (2/2)")

View File

@ -21,7 +21,7 @@ import click
from platformio import exception from platformio import exception
from platformio.commands.run import cli as cmd_run from platformio.commands.run import cli as cmd_run
from platformio.commands.run import print_header from platformio.commands.run.helpers import print_header
from platformio.project.helpers import get_project_test_dir from platformio.project.helpers import get_project_test_dir
TRANSPORT_OPTIONS = { TRANSPORT_OPTIONS = {
@ -82,7 +82,7 @@ class TestProcessorBase(object):
def __init__(self, cmd_ctx, testname, envname, options): def __init__(self, cmd_ctx, testname, envname, options):
self.cmd_ctx = cmd_ctx self.cmd_ctx = cmd_ctx
self.cmd_ctx.meta['piotest_processor'] = True self.cmd_ctx.meta['piotest_processor'] = True # FIXME
self.test_name = testname self.test_name = testname
self.options = options self.options = options
self.env_name = envname self.env_name = envname
@ -92,9 +92,10 @@ class TestProcessorBase(object):
self._outputcpp_generated = False self._outputcpp_generated = False
def get_transport(self): def get_transport(self):
transport = self.env_options.get("framework")
if self.env_options.get("platform") == "native": if self.env_options.get("platform") == "native":
transport = "native" transport = "native"
elif "framework" in self.env_options:
transport = self.env_options.get("framework")[0]
if "test_transport" in self.env_options: if "test_transport" in self.env_options:
transport = self.env_options['test_transport'] transport = self.env_options['test_transport']
if transport not in TRANSPORT_OPTIONS: if transport not in TRANSPORT_OPTIONS:
@ -108,8 +109,9 @@ class TestProcessorBase(object):
def print_progress(self, text, is_error=False): def print_progress(self, text, is_error=False):
click.echo() click.echo()
print_header( print_header(
"[test/%s] %s" % (click.style( "[test/%s > %s] %s" % (click.style(self.test_name, fg="yellow"),
self.test_name, fg="yellow", bold=True), text), click.style(self.env_name, fg="cyan"),
text),
is_error=is_error) is_error=is_error)
def build_or_upload(self, target): def build_or_upload(self, target):
@ -118,19 +120,22 @@ class TestProcessorBase(object):
self._outputcpp_generated = True self._outputcpp_generated = True
if self.test_name != "*": if self.test_name != "*":
self.cmd_ctx.meta['piotest'] = self.test_name self.cmd_ctx.meta['piotest'] = self.test_name # FIXME
if not self.options['verbose']: if not self.options['verbose']:
click.echo("Please wait...") click.echo("Please wait...")
return self.cmd_ctx.invoke( try:
cmd_run, return self.cmd_ctx.invoke(
project_dir=self.options['project_dir'], cmd_run,
upload_port=self.options['upload_port'], project_dir=self.options['project_dir'],
silent=not self.options['verbose'], upload_port=self.options['upload_port'],
environment=[self.env_name], silent=not self.options['verbose'],
disable_auto_clean="nobuild" in target, environment=[self.env_name],
target=target) disable_auto_clean="nobuild" in target,
target=target)
except exception.ReturnErrorCode:
return False
def process(self): def process(self):
raise NotImplementedError raise NotImplementedError

View File

@ -29,6 +29,7 @@ from platformio.managers.core import get_core_package_dir
from platformio.managers.package import BasePkgManager, PackageManager from platformio.managers.package import BasePkgManager, PackageManager
from platformio.proc import (BuildAsyncPipe, copy_pythonpath_to_osenv, from platformio.proc import (BuildAsyncPipe, copy_pythonpath_to_osenv,
exec_command, get_pythonexe_path) exec_command, get_pythonexe_path)
from platformio.project.config import ProjectConfig
from platformio.project.helpers import ( from platformio.project.helpers import (
get_project_boards_dir, get_project_core_dir, get_project_packages_dir, get_project_boards_dir, get_project_core_dir, get_project_packages_dir,
get_project_platforms_dir) get_project_platforms_dir)
@ -358,7 +359,12 @@ class PlatformRunMixin(object):
assert isinstance(variables, dict) assert isinstance(variables, dict)
assert isinstance(targets, list) assert isinstance(targets, list)
self.configure_default_packages(variables, targets) config = ProjectConfig.get_instance(variables['project_config'])
options = config.items(env=variables['pioenv'], as_dict=True)
if "framework" in options:
# support PIO Core 3.0 dev/platforms
options['pioframework'] = options['framework']
self.configure_default_packages(options, targets)
self.install_packages(silent=True) self.install_packages(silent=True)
self.silent = silent self.silent = silent
@ -611,12 +617,9 @@ class PlatformBase( # pylint: disable=too-many-public-methods
def get_package_type(self, name): def get_package_type(self, name):
return self.packages[name].get("type") return self.packages[name].get("type")
def configure_default_packages(self, variables, targets): def configure_default_packages(self, options, targets):
# enable used frameworks # enable used frameworks
frameworks = variables.get("pioframework", []) for framework in options.get("framework", []):
if not isinstance(frameworks, list):
frameworks = frameworks.split(", ")
for framework in frameworks:
if not self.frameworks: if not self.frameworks:
continue continue
framework = framework.lower().strip() framework = framework.lower().strip()

View File

@ -21,6 +21,7 @@ from os.path import isfile
import click import click
from platformio import exception from platformio import exception
from platformio.project.options import ProjectOptions
try: try:
import ConfigParser as ConfigParser import ConfigParser as ConfigParser
@ -39,98 +40,6 @@ CONFIG_HEADER = """;PlatformIO Project Configuration File
""" """
KNOWN_PLATFORMIO_OPTIONS = [
"description",
"env_default",
"extra_configs",
# Dirs
"core_dir",
"globallib_dir",
"platforms_dir",
"packages_dir",
"cache_dir",
"workspace_dir",
"build_dir",
"libdeps_dir",
"lib_dir",
"include_dir",
"src_dir",
"test_dir",
"boards_dir",
"data_dir"
]
KNOWN_ENV_OPTIONS = [
# Generic
"platform",
"framework",
"board",
"targets",
# Build
"build_flags",
"src_build_flags",
"build_unflags",
"src_filter",
# Upload
"upload_port",
"upload_protocol",
"upload_speed",
"upload_flags",
"upload_resetmethod",
# Monitor
"monitor_port",
"monitor_speed",
"monitor_rts",
"monitor_dtr",
"monitor_flags",
# Library
"lib_deps",
"lib_ignore",
"lib_extra_dirs",
"lib_ldf_mode",
"lib_compat_mode",
"lib_archive",
# Test
"piotest",
"test_filter",
"test_ignore",
"test_port",
"test_speed",
"test_transport",
"test_build_project_src",
# Debug
"debug_tool",
"debug_init_break",
"debug_init_cmds",
"debug_extra_cmds",
"debug_load_cmd",
"debug_load_mode",
"debug_server",
"debug_port",
"debug_svd_path",
# Other
"extra_scripts"
]
RENAMED_OPTIONS = {
"lib_use": "lib_deps",
"lib_force": "lib_deps",
"extra_script": "extra_scripts",
"monitor_baud": "monitor_speed",
"board_mcu": "board_build.mcu",
"board_f_cpu": "board_build.f_cpu",
"board_f_flash": "board_build.f_flash",
"board_flash_mode": "board_build.flash_mode"
}
class ProjectConfig(object): class ProjectConfig(object):
@ -191,11 +100,7 @@ class ProjectConfig(object):
return return
# load extra configs # load extra configs
if (not self._parser.has_section("platformio") for pattern in self.get("platformio", "extra_configs", []):
or not self._parser.has_option("platformio", "extra_configs")):
return
extra_configs = self.getlist("platformio", "extra_configs")
for pattern in extra_configs:
for item in glob.glob(pattern): for item in glob.glob(pattern):
self.read(item) self.read(item)
@ -212,6 +117,14 @@ class ProjectConfig(object):
if option not in options: if option not in options:
options.append(option) options.append(option)
# handle system environment variables
scope = section.split(":", 1)[0]
for option_meta in ProjectOptions.values():
if option_meta.scope != scope or option_meta.name in options:
continue
if option_meta.sysenvvar and option_meta.sysenvvar in os.environ:
options.append(option_meta.name)
return options return options
def has_option(self, section, option): def has_option(self, section, option):
@ -239,39 +152,84 @@ class ProjectConfig(object):
value = "\n" + value # start from a new line value = "\n" + value # start from a new line
self._parser.set(section, option, value) self._parser.set(section, option, value)
def get(self, section, option): def getraw(self, section, option):
if not self.expand_interpolations: if not self.expand_interpolations:
return self._parser.get(section, option) return self._parser.get(section, option)
try: try:
value = self._parser.get(section, option) value = self._parser.get(section, option)
except ConfigParser.NoOptionError: except ConfigParser.NoOptionError as e:
if not section.startswith("env:"):
raise e
value = self._parser.get("env", option) value = self._parser.get("env", option)
except ConfigParser.Error as e:
raise exception.InvalidProjectConf(self.path, str(e))
if "${" not in value or "}" not in value: if "${" not in value or "}" not in value:
return value return value
return self.VARTPL_RE.sub(self._re_sub_handler, value) return self.VARTPL_RE.sub(self._re_interpolation_handler, value)
def _re_sub_handler(self, match): def _re_interpolation_handler(self, match):
section, option = match.group(1), match.group(2) section, option = match.group(1), match.group(2)
if section == "sysenv": if section == "sysenv":
return os.getenv(option) return os.getenv(option)
return self.get(section, option) return self.getraw(section, option)
def getlist(self, section, option): def get(self, section, option, default=None):
return self.parse_multi_values(self.get(section, option)) value = None
try:
value = self.getraw(section, option)
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
pass # handle value from system environment
except ConfigParser.Error as e:
raise exception.InvalidProjectConf(self.path, str(e))
option_meta = ProjectOptions.get(
"%s.%s" % (section.split(":", 1)[0], option))
if not option_meta:
return value or default
if value and option_meta.multiple:
value = self.parse_multi_values(value)
if option_meta.sysenvvar:
envvar_value = os.getenv(option_meta.sysenvvar)
if not envvar_value and option_meta.oldnames:
for oldoption in option_meta.oldnames:
envvar_value = os.getenv("PLATFORMIO_" + oldoption.upper())
if envvar_value:
break
if envvar_value and option_meta.multiple:
value = value or []
value.extend(self.parse_multi_values(envvar_value))
elif envvar_value and not value:
value = envvar_value
# option is not specified by user
if value is None:
return default
return self._covert_value(value, option_meta.type)
@staticmethod
def _covert_value(value, to_type):
items = value
if not isinstance(value, (list, tuple)):
items = [value]
for i, v in enumerate(items):
if to_type == bool:
items[i] = v in ("1", "true", "yes", "y")
elif to_type == int:
items[i] = int(v)
elif to_type == float:
items[i] = float(v)
return items if isinstance(value, (list, tuple)) else items[0]
def envs(self): def envs(self):
return [s[4:] for s in self._parser.sections() if s.startswith("env:")] return [s[4:] for s in self._parser.sections() if s.startswith("env:")]
def default_envs(self): def default_envs(self):
if not self._parser.has_option("platformio", "env_default"): return self.get("platformio", "env_default", [])
return []
return self.getlist("platformio", "env_default")
def validate(self, envs=None, validate_options=True): def validate(self, envs=None, silent=False):
if not isfile(self.path): if not isfile(self.path):
raise exception.NotPlatformIOProject(self.path) raise exception.NotPlatformIOProject(self.path)
# check envs # check envs
@ -283,74 +241,65 @@ class ProjectConfig(object):
if unknown: if unknown:
raise exception.UnknownEnvNames(", ".join(unknown), raise exception.UnknownEnvNames(", ".join(unknown),
", ".join(known)) ", ".join(known))
return self.validate_options() if validate_options else True return self.validate_options(silent)
def validate_options(self): def validate_options(self, silent=False):
return (self._validate_platformio_options() warnings = []
and self._validate_env_options()) # legacy `lib_extra_dirs` in [platformio]
if (self._parser.has_section("platformio")
def _validate_platformio_options(self): and self._parser.has_option("platformio", "lib_extra_dirs")):
if not self._parser.has_section("platformio"):
return True
warnings = set()
# legacy `lib_extra_dirs`
if self._parser.has_option("platformio", "lib_extra_dirs"):
if not self._parser.has_section("env"): if not self._parser.has_section("env"):
self._parser.add_section("env") self._parser.add_section("env")
self._parser.set("env", "lib_extra_dirs", self._parser.set("env", "lib_extra_dirs",
self._parser.get("platformio", "lib_extra_dirs")) self._parser.get("platformio", "lib_extra_dirs"))
self._parser.remove_option("platformio", "lib_extra_dirs") self._parser.remove_option("platformio", "lib_extra_dirs")
warnings.add( warnings.append(
"`lib_extra_dirs` option is deprecated in section " "`lib_extra_dirs` configuration option is deprecated in "
"`platformio`! Please move it to global `env` section") "section [platformio]! Please move it to global `env` section")
unknown = set(k for k, _ in self.items("platformio")) - set( warnings.extend(self._validate_unknown_options())
KNOWN_PLATFORMIO_OPTIONS)
if unknown:
warnings.add(
"Ignore unknown `%s` options in section `[platformio]`" %
", ".join(unknown))
for warning in warnings: if not silent:
click.secho("Warning! %s" % warning, fg="yellow") for warning in warnings:
click.secho("Warning! %s" % warning, fg="yellow")
return True return warnings
def _validate_env_options(self): def _validate_unknown_options(self):
warnings = set() warnings = []
renamed_options = {}
for option in ProjectOptions.values():
if option.oldnames:
renamed_options.update(
{name: option.name
for name in option.oldnames})
for section in self._parser.sections(): for section in self._parser.sections():
if section != "env" and not section.startswith("env:"):
continue
for option in self._parser.options(section): for option in self._parser.options(section):
# obsolete # obsolete
if option in RENAMED_OPTIONS: if option in renamed_options:
warnings.add( warnings.append(
"`%s` option in section `[%s]` is deprecated and will " "`%s` configuration option in section [%s] is "
"be removed in the next release! Please use `%s` " "deprecated and will be removed in the next release! "
"instead" % (option, section, RENAMED_OPTIONS[option])) "Please use `%s` instead" % (option, section,
renamed_options[option]))
# rename on-the-fly # rename on-the-fly
self._parser.set(section, RENAMED_OPTIONS[option], self._parser.set(section, renamed_options[option],
self._parser.get(section, option)) self._parser.get(section, option))
self._parser.remove_option(section, option) self._parser.remove_option(section, option)
continue continue
# unknown # unknown
scope = section.split(":", 1)[0]
unknown_conditions = [ unknown_conditions = [
option not in KNOWN_ENV_OPTIONS, ("%s.%s" % (scope, option)) not in ProjectOptions,
not option.startswith("custom_"), scope != "env" or
not option.startswith("board_") not option.startswith(("custom_", "board_"))
] # yapf: disable ] # yapf: disable
if all(unknown_conditions): if all(unknown_conditions):
warnings.add( warnings.append("Ignore unknown configuration option `%s` "
"Detected non-PlatformIO `%s` option in `[%s]` section" "in section [%s]" % (option, section))
% (option, section)) return warnings
for warning in warnings:
click.secho("Warning! %s" % warning, fg="yellow")
return True
def to_json(self): def to_json(self):
result = {} result = {}

View File

@ -15,7 +15,7 @@
import os import os
from hashlib import sha1 from hashlib import sha1
from os import walk from os import walk
from os.path import (abspath, dirname, expanduser, isdir, isfile, join, from os.path import (dirname, expanduser, isdir, isfile, join, realpath,
splitdrive) splitdrive)
from platformio import __version__ from platformio import __version__
@ -44,46 +44,31 @@ def find_project_dir_above(path):
def get_project_optional_dir(name, default=None): def get_project_optional_dir(name, default=None):
paths = None project_dir = get_project_dir()
config = ProjectConfig.get_instance(join(project_dir, "platformio.ini"))
optional_dir = config.get("platformio", name)
# check for system environment variable if not optional_dir:
var_name = "PLATFORMIO_%s" % name.upper()
if var_name in os.environ:
paths = os.getenv(var_name)
config = ProjectConfig.get_instance(
join(get_project_dir(), "platformio.ini"))
if (config.has_section("platformio")
and config.has_option("platformio", name)):
paths = config.get("platformio", name)
if not paths:
return default return default
items = [] if "$PROJECT_HASH" in optional_dir:
for item in paths.split(", "): optional_dir = optional_dir.replace(
if item.startswith("~"):
item = expanduser(item)
items.append(abspath(item))
paths = ", ".join(items)
while "$PROJECT_HASH" in paths:
project_dir = get_project_dir()
paths = paths.replace(
"$PROJECT_HASH", "$PROJECT_HASH",
sha1(project_dir if PY2 else project_dir.encode()).hexdigest() sha1(project_dir if PY2 else project_dir.encode()).hexdigest()
[:10]) [:10])
return paths if optional_dir.startswith("~"):
optional_dir = expanduser(optional_dir)
return realpath(optional_dir)
def get_project_core_dir(): def get_project_core_dir():
default = join(expanduser("~"), ".platformio")
core_dir = get_project_optional_dir( core_dir = get_project_optional_dir(
"core_dir", "core_dir", get_project_optional_dir("home_dir", default))
get_project_optional_dir("home_dir",
join(expanduser("~"), ".platformio")))
win_core_dir = None win_core_dir = None
if WINDOWS: if WINDOWS and core_dir == default:
win_core_dir = splitdrive(core_dir)[0] + "\\.platformio" win_core_dir = splitdrive(core_dir)[0] + "\\.platformio"
if isdir(win_core_dir): if isdir(win_core_dir):
core_dir = win_core_dir core_dir = win_core_dir
@ -91,10 +76,12 @@ def get_project_core_dir():
if not isdir(core_dir): if not isdir(core_dir):
try: try:
os.makedirs(core_dir) os.makedirs(core_dir)
except: # pylint: disable=bare-except except OSError as e:
if win_core_dir: if win_core_dir:
os.makedirs(win_core_dir) os.makedirs(win_core_dir)
core_dir = win_core_dir core_dir = win_core_dir
else:
raise e
assert isdir(core_dir) assert isdir(core_dir)
return core_dir return core_dir

View File

@ -0,0 +1,198 @@
# 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.
# pylint: disable=redefined-builtin, too-many-arguments
from collections import OrderedDict, namedtuple
ConfigOptionClass = namedtuple("ConfigOption", [
"scope", "name", "type", "multiple", "sysenvvar", "buildenvvar", "oldnames"
])
def ConfigOption(scope,
name,
type=str,
multiple=False,
sysenvvar=None,
buildenvvar=None,
oldnames=None):
return ConfigOptionClass(scope, name, type, multiple, sysenvvar,
buildenvvar, oldnames)
def ConfigPlatformioOption(*args, **kwargs):
return ConfigOption("platformio", *args, **kwargs)
def ConfigEnvOption(*args, **kwargs):
return ConfigOption("env", *args, **kwargs)
ProjectOptions = OrderedDict([
("%s.%s" % (option.scope, option.name), option) for option in [
#
# [platformio]
#
ConfigPlatformioOption(name="description"),
ConfigPlatformioOption(
name="env_default",
multiple=True,
sysenvvar="PLATFORMIO_ENV_DEFAULT"),
ConfigPlatformioOption(name="extra_configs", multiple=True),
# Dirs
ConfigPlatformioOption(
name="core_dir",
oldnames=["home_dir"],
sysenvvar="PLATFORMIO_CORE_DIR"),
ConfigPlatformioOption(
name="globallib_dir", sysenvvar="PLATFORMIO_GLOBALLIB_DIR"),
ConfigPlatformioOption(
name="platforms_dir", sysenvvar="PLATFORMIO_PLATFORMS_DIR"),
ConfigPlatformioOption(
name="packages_dir", sysenvvar="PLATFORMIO_PACKAGES_DIR"),
ConfigPlatformioOption(
name="cache_dir", sysenvvar="PLATFORMIO_CACHE_DIR"),
ConfigPlatformioOption(
name="workspace_dir", sysenvvar="PLATFORMIO_WORKSPACE_DIR"),
ConfigPlatformioOption(
name="build_dir", sysenvvar="PLATFORMIO_BUILD_DIR"),
ConfigPlatformioOption(
name="libdeps_dir", sysenvvar="PLATFORMIO_LIBDEPS_DIR"),
ConfigPlatformioOption(name="lib_dir", sysenvvar="PLATFORMIO_LIB_DIR"),
ConfigPlatformioOption(
name="include_dir", sysenvvar="PLATFORMIO_INCLUDE_DIR"),
ConfigPlatformioOption(name="src_dir", sysenvvar="PLATFORMIO_SRC_DIR"),
ConfigPlatformioOption(
name="test_dir", sysenvvar="PLATFORMIO_TEST_DIR"),
ConfigPlatformioOption(
name="boards_dir", sysenvvar="PLATFORMIO_BOARDS_DIR"),
ConfigPlatformioOption(
name="data_dir", sysenvvar="PLATFORMIO_DATA_DIR"),
#
# [env]
#
# Generic
ConfigEnvOption(name="platform", buildenvvar="PIOPLATFORM"),
ConfigEnvOption(
name="framework", multiple=True, buildenvvar="PIOFRAMEWORK"),
ConfigEnvOption(name="targets", multiple=True),
# Board
ConfigEnvOption(name="board", buildenvvar="BOARD"),
ConfigEnvOption(
name="board_build.mcu",
oldnames=["board_mcu"],
buildenvvar="BOARD_MCU"),
ConfigEnvOption(
name="board_build.f_cpu",
oldnames=["board_f_cpu"],
buildenvvar="BOARD_F_CPU"),
ConfigEnvOption(
name="board_build.f_flash",
oldnames=["board_f_flash"],
buildenvvar="BOARD_F_FLASH"),
ConfigEnvOption(
name="board_build.flash_mode",
oldnames=["board_flash_mode"],
buildenvvar="BOARD_FLASH_MODE"),
# Build
ConfigEnvOption(
name="build_flags",
multiple=True,
sysenvvar="PLATFORMIO_BUILD_FLAGS",
buildenvvar="BUILD_FLAGS"),
ConfigEnvOption(
name="src_build_flags",
multiple=True,
sysenvvar="PLATFORMIO_SRC_BUILD_FLAGS",
buildenvvar="SRC_BUILD_FLAGS"),
ConfigEnvOption(
name="build_unflags",
multiple=True,
sysenvvar="PLATFORMIO_BUILD_UNFLAGS",
buildenvvar="BUILD_UNFLAGS"),
ConfigEnvOption(
name="src_filter",
multiple=True,
sysenvvar="PLATFORMIO_SRC_FILTER",
buildenvvar="SRC_FILTER"),
# Upload
ConfigEnvOption(
name="upload_port",
sysenvvar="PLATFORMIO_UPLOAD_PORT",
buildenvvar="UPLOAD_PORT"),
ConfigEnvOption(name="upload_protocol", buildenvvar="UPLOAD_PROTOCOL"),
ConfigEnvOption(name="upload_speed", buildenvvar="UPLOAD_SPEED"),
ConfigEnvOption(
name="upload_flags",
multiple=True,
sysenvvar="PLATFORMIO_UPLOAD_FLAGS",
buildenvvar="UPLOAD_FLAGS"),
ConfigEnvOption(
name="upload_resetmethod", buildenvvar="UPLOAD_RESETMETHOD"),
# Monitor
ConfigEnvOption(name="monitor_port"),
ConfigEnvOption(name="monitor_speed", oldnames=["monitor_baud"]),
ConfigEnvOption(name="monitor_rts"),
ConfigEnvOption(name="monitor_dtr"),
ConfigEnvOption(name="monitor_flags", multiple=True),
# Library
ConfigEnvOption(
name="lib_deps",
oldnames=["lib_use", "lib_force", "lib_install"],
multiple=True),
ConfigEnvOption(name="lib_ignore", multiple=True),
ConfigEnvOption(
name="lib_extra_dirs",
multiple=True,
sysenvvar="PLATFORMIO_LIB_EXTRA_DIRS"),
ConfigEnvOption(name="lib_ldf_mode"),
ConfigEnvOption(name="lib_compat_mode"),
ConfigEnvOption(name="lib_archive", type=bool),
# Test
ConfigEnvOption(name="test_filter", multiple=True),
ConfigEnvOption(name="test_ignore", multiple=True),
ConfigEnvOption(name="test_port"),
ConfigEnvOption(name="test_speed"),
ConfigEnvOption(name="test_transport"),
ConfigEnvOption(name="test_build_project_src"),
# Debug
ConfigEnvOption(name="debug_tool"),
ConfigEnvOption(name="debug_init_break"),
ConfigEnvOption(name="debug_init_cmds", multiple=True),
ConfigEnvOption(name="debug_extra_cmds", multiple=True),
ConfigEnvOption(name="debug_load_cmd"),
ConfigEnvOption(name="debug_load_mode"),
ConfigEnvOption(name="debug_server"),
ConfigEnvOption(name="debug_port"),
ConfigEnvOption(name="debug_svd_path"),
# Other
ConfigEnvOption(
name="extra_scripts",
oldnames=["extra_script"],
multiple=True,
sysenvvar="PLATFORMIO_EXTRA_SCRIPTS")
]
])

View File

@ -310,12 +310,15 @@ def measure_ci():
def on_run_environment(options, targets): def on_run_environment(options, targets):
opts = [ non_sensative_values = ["board", "platform", "framework"]
"%s=%s" % (opt, value.replace("\n", ", ") if "\n" in value else value) safe_options = []
for opt, value in sorted(options.items()) for key, value in sorted(options.items()):
] if key in non_sensative_values:
safe_options.append("%s=%s" % (key, value))
else:
safe_options.append(key)
targets = [t.title() for t in targets or ["run"]] targets = [t.title() for t in targets or ["run"]]
on_event("Env", " ".join(targets), "&".join(opts)) on_event("Env", " ".join(targets), "&".join(safe_options))
def on_event(category, action, label=None, value=None, screen_name=None): def on_event(category, action, label=None, value=None, screen_name=None):

View File

@ -20,7 +20,7 @@ from platformio.commands.lib import cli as cmd_lib
def test_ci_empty(clirunner): def test_ci_empty(clirunner):
result = clirunner.invoke(cmd_ci) result = clirunner.invoke(cmd_ci)
assert result.exit_code == 2 assert result.exit_code != 0
assert "Invalid value: Missing argument 'src'" in result.output assert "Invalid value: Missing argument 'src'" in result.output

View File

@ -16,7 +16,7 @@ import json
from os import getcwd, makedirs from os import getcwd, makedirs
from os.path import getsize, isdir, isfile, join from os.path import getsize, isdir, isfile, join
from platformio import exception, util from platformio import exception
from platformio.commands.boards import cli as cmd_boards from platformio.commands.boards import cli as cmd_boards
from platformio.commands.init import cli as cmd_init from platformio.commands.init import cli as cmd_init
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
@ -109,13 +109,13 @@ def test_init_special_board(clirunner, validate_cliresult):
config = ProjectConfig(join(getcwd(), "platformio.ini")) config = ProjectConfig(join(getcwd(), "platformio.ini"))
config.validate() config.validate()
expected_result = [("platform", str(boards[0]['platform'])),
("framework",
str(boards[0]['frameworks'][0])), ("board", "uno")]
expected_result = dict(platform=str(boards[0]['platform']),
board="uno",
framework=[str(boards[0]['frameworks'][0])])
assert config.has_section("env:uno") assert config.has_section("env:uno")
assert not set(expected_result).symmetric_difference( assert sorted(config.items(env="uno", as_dict=True).items()) == sorted(
set(config.items("env:uno"))) expected_result.items())
def test_init_enable_auto_uploading(clirunner, validate_cliresult): def test_init_enable_auto_uploading(clirunner, validate_cliresult):
@ -126,11 +126,13 @@ def test_init_enable_auto_uploading(clirunner, validate_cliresult):
validate_pioproject(getcwd()) validate_pioproject(getcwd())
config = ProjectConfig(join(getcwd(), "platformio.ini")) config = ProjectConfig(join(getcwd(), "platformio.ini"))
config.validate() config.validate()
expected_result = [("platform", "atmelavr"), ("framework", "arduino"), expected_result = dict(targets=["upload"],
("board", "uno"), ("targets", "upload")] platform="atmelavr",
board="uno",
framework=["arduino"])
assert config.has_section("env:uno") assert config.has_section("env:uno")
assert not set(expected_result).symmetric_difference( assert sorted(config.items(env="uno", as_dict=True).items()) == sorted(
set(config.items("env:uno"))) expected_result.items())
def test_init_custom_framework(clirunner, validate_cliresult): def test_init_custom_framework(clirunner, validate_cliresult):
@ -141,11 +143,13 @@ def test_init_custom_framework(clirunner, validate_cliresult):
validate_pioproject(getcwd()) validate_pioproject(getcwd())
config = ProjectConfig(join(getcwd(), "platformio.ini")) config = ProjectConfig(join(getcwd(), "platformio.ini"))
config.validate() config.validate()
expected_result = [("platform", "teensy"), ("framework", "mbed"), expected_result = dict(platform="teensy",
("board", "teensy31")] board="teensy31",
framework=["mbed"])
assert config.has_section("env:teensy31") assert config.has_section("env:teensy31")
assert not set(expected_result).symmetric_difference( assert sorted(config.items(env="teensy31",
set(config.items("env:teensy31"))) as_dict=True).items()) == sorted(
expected_result.items())
def test_init_incorrect_board(clirunner): def test_init_incorrect_board(clirunner):

View File

@ -14,7 +14,9 @@
import os import os
from platformio.project.config import ProjectConfig import pytest
from platformio.project.config import ConfigParser, ProjectConfig
BASE_CONFIG = """ BASE_CONFIG = """
[platformio] [platformio]
@ -26,7 +28,9 @@ extra_configs =
# global options per [env:*] # global options per [env:*]
[env] [env]
monitor_speed = 115200 monitor_speed = 115200
lib_deps = Lib1, Lib2 lib_deps =
Lib1
Lib2
lib_ignore = ${custom.lib_ignore} lib_ignore = ${custom.lib_ignore}
[custom] [custom]
@ -46,6 +50,7 @@ build_flags = ${custom.lib_flags} ${custom.debug_flags}
[env:extra_2] [env:extra_2]
build_flags = ${custom.debug_flags} ${custom.extra_flags} build_flags = ${custom.debug_flags} ${custom.extra_flags}
lib_ignore = ${env.lib_ignore}, Lib3 lib_ignore = ${env.lib_ignore}, Lib3
upload_port = /dev/extra_2/port
""" """
EXTRA_DEBUG_CONFIG = """ EXTRA_DEBUG_CONFIG = """
@ -58,7 +63,7 @@ build_flags = -Og
""" """
def test_parser(tmpdir): def test_real_config(tmpdir):
tmpdir.join("platformio.ini").write(BASE_CONFIG) tmpdir.join("platformio.ini").write(BASE_CONFIG)
tmpdir.join("extra_envs.ini").write(EXTRA_ENVS_CONFIG) tmpdir.join("extra_envs.ini").write(EXTRA_ENVS_CONFIG)
tmpdir.join("extra_debug.ini").write(EXTRA_DEBUG_CONFIG) tmpdir.join("extra_debug.ini").write(EXTRA_DEBUG_CONFIG)
@ -68,6 +73,16 @@ def test_parser(tmpdir):
config = ProjectConfig(tmpdir.join("platformio.ini").strpath) config = ProjectConfig(tmpdir.join("platformio.ini").strpath)
assert config assert config
# unknown section
with pytest.raises(ConfigParser.NoSectionError):
config.getraw("unknown_section", "unknown_option")
# unknown option
with pytest.raises(ConfigParser.NoOptionError):
config.getraw("custom", "unknown_option")
# unknown option even if exists in [env]
with pytest.raises(ConfigParser.NoOptionError):
config.getraw("platformio", "monitor_speed")
# sections # sections
assert config.sections() == [ assert config.sections() == [
"platformio", "env", "custom", "env:base", "env:extra_1", "env:extra_2" "platformio", "env", "custom", "env:base", "env:extra_1", "env:extra_2"
@ -87,30 +102,76 @@ def test_parser(tmpdir):
assert not config.has_option("custom", "monitor_speed") assert not config.has_option("custom", "monitor_speed")
# sysenv # sysenv
assert config.get("custom", "extra_flags") == "" assert config.get("custom", "extra_flags") is None
assert config.get("env:base", "build_flags") == ["-D DEBUG=1"]
assert config.get("env:base", "upload_port") is None
assert config.get("env:extra_2", "upload_port") == "/dev/extra_2/port"
os.environ["PLATFORMIO_BUILD_FLAGS"] = "-DSYSENVDEPS1 -DSYSENVDEPS2"
os.environ["PLATFORMIO_UPLOAD_PORT"] = "/dev/sysenv/port"
os.environ["__PIO_TEST_CNF_EXTRA_FLAGS"] = "-L /usr/local/lib" os.environ["__PIO_TEST_CNF_EXTRA_FLAGS"] = "-L /usr/local/lib"
assert config.get("custom", "extra_flags") == "-L /usr/local/lib" assert config.get("custom", "extra_flags") == "-L /usr/local/lib"
assert config.get("env:base", "build_flags") == [
"-D DEBUG=1 -L /usr/local/lib", "-DSYSENVDEPS1 -DSYSENVDEPS2"
]
assert config.get("env:base", "upload_port") == "/dev/sysenv/port"
assert config.get("env:extra_2", "upload_port") == "/dev/extra_2/port"
# getraw
assert config.getraw("env:extra_1", "lib_deps") == "\nLib1\nLib2"
assert config.getraw("env:extra_1", "build_flags") == "-lc -lm -D DEBUG=1"
# get # get
assert config.get("custom", "debug_flags") == "-D DEBUG=1" assert config.get("custom", "debug_flags") == "-D DEBUG=1"
assert config.get("env:extra_1", "build_flags") == "-lc -lm -D DEBUG=1" assert config.get("env:extra_1", "build_flags") == [
assert config.get("env:extra_2", "build_flags") == "-Og" "-lc -lm -D DEBUG=1", "-DSYSENVDEPS1 -DSYSENVDEPS2"
]
assert config.get("env:extra_2", "build_flags") == [
"-Og", "-DSYSENVDEPS1 -DSYSENVDEPS2"]
assert config.get("env:extra_2", "monitor_speed") == "115200" assert config.get("env:extra_2", "monitor_speed") == "115200"
assert config.get("env:base", assert config.get("env:base", "build_flags") == ([
"build_flags") == ("-D DEBUG=1 -L /usr/local/lib") "-D DEBUG=1 -L /usr/local/lib", "-DSYSENVDEPS1 -DSYSENVDEPS2"
])
# items # items
assert config.items("custom") == [("debug_flags", "-D DEBUG=1"), assert config.items("custom") == [
("lib_flags", "-lc -lm"), ("debug_flags", "-D DEBUG=1"),
("extra_flags", "-L /usr/local/lib"), ("lib_flags", "-lc -lm"),
("lib_ignore", "LibIgnoreCustom")] ("extra_flags", "-L /usr/local/lib"),
assert config.items(env="extra_1") == [("build_flags", ("lib_ignore", "LibIgnoreCustom")
"-lc -lm -D DEBUG=1"), ] # yapf: disable
("monitor_speed", "115200"), assert config.items(env="extra_1") == [
("lib_deps", "Lib1, Lib2"), ("build_flags", ["-lc -lm -D DEBUG=1", "-DSYSENVDEPS1 -DSYSENVDEPS2"]),
("lib_ignore", "LibIgnoreCustom")] ("monitor_speed", "115200"),
assert config.items(env="extra_2") == [("build_flags", "-Og"), ("lib_deps", ["Lib1", "Lib2"]),
("lib_ignore", ("lib_ignore", ["LibIgnoreCustom"]),
"LibIgnoreCustom, Lib3"), ("upload_port", "/dev/sysenv/port")
("monitor_speed", "115200"), ] # yapf: disable
("lib_deps", "Lib1, Lib2")] assert config.items(env="extra_2") == [
("build_flags", ["-Og", "-DSYSENVDEPS1 -DSYSENVDEPS2"]),
("lib_ignore", ["LibIgnoreCustom", "Lib3"]),
("upload_port", "/dev/extra_2/port"),
("monitor_speed", "115200"),
("lib_deps", ["Lib1", "Lib2"])
] # yapf: disable
# cleanup system environment variables
del os.environ["PLATFORMIO_BUILD_FLAGS"]
del os.environ["PLATFORMIO_UPLOAD_PORT"]
del os.environ["__PIO_TEST_CNF_EXTRA_FLAGS"]
def test_empty_config():
config = ProjectConfig("/non/existing/platformio.ini")
# unknown section
with pytest.raises(ConfigParser.NoSectionError):
config.getraw("unknown_section", "unknown_option")
assert config.sections() == []
assert config.get("section", "option") is None
assert config.get("section", "option", 13) == 13
# sysenv
os.environ["PLATFORMIO_HOME_DIR"] = "/custom/core/dir"
assert config.get("platformio", "core_dir") == "/custom/core/dir"
del os.environ["PLATFORMIO_HOME_DIR"]