From c235974eb63253f2a501fa4289bc2ac25bbd8f96 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 7 May 2019 17:51:50 +0300 Subject: [PATCH] Switch to the new ProjectConfig API --- platformio/commands/ci.py | 5 +- platformio/commands/device.py | 38 ++---- platformio/commands/init.py | 22 ++-- platformio/commands/run.py | 99 ++------------ platformio/exception.py | 167 ++++++++++++++---------- platformio/ide/projectgenerator.py | 14 +- platformio/project/config.py | 199 +++++++++++++++++++++++++++-- platformio/telemetry.py | 9 +- platformio/util.py | 7 +- tests/conftest.py | 6 +- tests/test_projectconf.py | 11 +- 11 files changed, 354 insertions(+), 223 deletions(-) diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index e1b60087..18845592 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -25,6 +25,7 @@ from platformio.commands.init import cli as cmd_init from platformio.commands.init import validate_boards from platformio.commands.run import cli as cmd_run from platformio.exception import CIBuildEnvsEmpty +from platformio.project.config import ProjectConfig def validate_path(ctx, param, value): # pylint: disable=unused-argument @@ -161,8 +162,8 @@ def _exclude_contents(dst_dir, patterns): def _copy_project_conf(build_dir, project_conf): - config = util.load_project_config(project_conf) + config = ProjectConfig(project_conf, parse_extra=False) if config.has_section("platformio"): config.remove_section("platformio") - with open(join(build_dir, "platformio.ini"), "w") as fp: + with open(join(build_dir, "platformio.ini"), "wb") as fp: config.write(fp) diff --git a/platformio/commands/device.py b/platformio/commands/device.py index 0a8ea38f..42bd4f6c 100644 --- a/platformio/commands/device.py +++ b/platformio/commands/device.py @@ -15,11 +15,13 @@ import json import sys from os import getcwd +from os.path import join import click from serial.tools import miniterm from platformio import exception, util +from platformio.project.config import ProjectConfig @click.group(short_help="Monitor device or list existing") @@ -161,11 +163,10 @@ def device_list( # pylint: disable=too-many-branches help="Load configuration from `platformio.ini` and specified environment") def device_monitor(**kwargs): # pylint: disable=too-many-branches try: - project_options = get_project_options(kwargs['project_dir'], + monitor_options = get_project_options(kwargs['project_dir'], kwargs['environment']) - monitor_options = {k: v for k, v in project_options or []} if monitor_options: - for k in ("port", "baud", "speed", "rts", "dtr"): + for k in ("port", "speed", "rts", "dtr"): k2 = "monitor_%s" % k if k == "speed": k = "baud" @@ -205,24 +206,13 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches raise exception.MinitermException(e) -def get_project_options(project_dir, environment): - config = util.load_project_config(project_dir) - if not config.sections(): - return None - - known_envs = [s[4:] for s in config.sections() if s.startswith("env:")] - if environment: - if environment in known_envs: - return config.items("env:%s" % environment) - raise exception.UnknownEnvNames(environment, ", ".join(known_envs)) - - if not known_envs: - return None - - if config.has_option("platformio", "env_default"): - env_default = config.get("platformio", - "env_default").split(", ")[0].strip() - if env_default and env_default in known_envs: - return config.items("env:%s" % env_default) - - return config.items("env:%s" % known_envs[0]) +def get_project_options(project_dir, environment=None): + config = ProjectConfig.get_instance(join(project_dir, "platformio.ini")) + config.validate(envs=[environment] if environment else None) + if not environment: + default_envs = config.default_envs() + if default_envs: + environment = default_envs[0] + else: + environment = config.envs()[0] + return {k: v for k, v in config.items(env=environment)} diff --git a/platformio/commands/init.py b/platformio/commands/init.py index e7127313..db45ddfc 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -23,9 +23,9 @@ import click from platformio import exception, util from platformio.commands.platform import \ platform_install as cli_platform_install -from platformio.commands.run import check_project_envs from platformio.ide.projectgenerator import ProjectGenerator from platformio.managers.platform import PlatformManager +from platformio.project.config import ProjectConfig def validate_boards(ctx, param, value): # pylint: disable=W0613 @@ -128,14 +128,11 @@ def cli( def get_best_envname(project_dir, boards=None): - config = util.load_project_config(project_dir) - env_default = None - if config.has_option("platformio", "env_default"): - env_default = util.parse_conf_multi_values( - config.get("platformio", "env_default")) - check_project_envs(config, env_default) - if env_default: - return env_default[0] + config = ProjectConfig(join(project_dir, "platformio.ini")) + config.validate() + default_envs = config.default_envs() + if default_envs: + return default_envs[0] section = None for section in config.sections(): if not section.startswith("env:"): @@ -369,7 +366,8 @@ def fill_project_envs(ctx, project_dir, board_ids, project_option, env_prefix, used_boards = [] used_platforms = [] - config = util.load_project_config(project_dir) + config_path = join(project_dir, "platformio.ini") + config = util.load_project_config(config_path) for section in config.sections(): cond = [ section.startswith("env:"), @@ -409,10 +407,12 @@ def fill_project_envs(ctx, project_dir, board_ids, project_option, env_prefix, if not content: return - with open(join(project_dir, "platformio.ini"), "a") as f: + with open(config_path, "a") as f: content.append("") f.write("\n".join(content)) + ProjectConfig.reset_instances() + def _install_dependent_platforms(ctx, platforms): installed_platforms = [ diff --git a/platformio/commands/run.py b/platformio/commands/run.py index 817df353..baff6a54 100644 --- a/platformio/commands/run.py +++ b/platformio/commands/run.py @@ -26,6 +26,7 @@ from platformio.commands.platform import \ platform_install as cmd_platform_install from platformio.managers.lib import LibraryManager, is_builtin_lib from platformio.managers.platform import PlatformFactory +from platformio.project.config import ProjectConfig # pylint: disable=too-many-arguments,too-many-locals,too-many-branches @@ -54,9 +55,6 @@ def cli(ctx, environment, target, upload_port, project_dir, silent, verbose, if isfile(project_dir): project_dir = util.find_project_dir_above(project_dir) - if not util.is_platformio_project(project_dir): - raise exception.NotPlatformIOProject(project_dir) - with util.cd(project_dir): # clean obsolete build dir if not disable_auto_clean: @@ -69,25 +67,17 @@ def cli(ctx, environment, target, upload_port, project_dir, silent, verbose, util.get_projectbuild_dir(force=True), fg="yellow") - config = util.load_project_config() - env_default = None - if config.has_option("platformio", "env_default"): - env_default = util.parse_conf_multi_values( - config.get("platformio", "env_default")) - - check_project_defopts(config) - check_project_envs(config, environment or env_default) + config = ProjectConfig.get_instance( + join(project_dir, "platformio.ini")) + config.validate() results = [] start_time = time() - for section in config.sections(): - if not section.startswith("env:"): - continue - - envname = section[4:] + default_envs = config.default_envs() + for envname in config.envs(): skipenv = any([ environment and envname not in environment, not environment - and env_default and envname not in env_default + and default_envs and envname not in default_envs ]) if skipenv: results.append((envname, None)) @@ -97,7 +87,7 @@ def cli(ctx, environment, target, upload_port, project_dir, silent, verbose, click.echo() options = {} - for k, v in config.items(section): + for k, v in config.items(env=envname): options[k] = v if "piotest" not in options and "piotest" in ctx.meta: options['piotest'] = ctx.meta['piotest'] @@ -127,26 +117,6 @@ class EnvironmentProcessor(object): DEFAULT_DUMP_OPTIONS = ("platform", "framework", "board") - KNOWN_PLATFORMIO_OPTIONS = [ - "description", "env_default", "home_dir", "lib_dir", "libdeps_dir", - "include_dir", "src_dir", "build_dir", "data_dir", "test_dir", - "boards_dir", "lib_extra_dirs", "extra_configs" - ] - - KNOWN_ENV_OPTIONS = [ - "platform", "framework", "board", "build_flags", "src_build_flags", - "build_unflags", "src_filter", "extra_scripts", "targets", - "upload_port", "upload_protocol", "upload_speed", "upload_flags", - "upload_resetmethod", "lib_deps", "lib_ignore", "lib_extra_dirs", - "lib_ldf_mode", "lib_compat_mode", "lib_archive", "piotest", - "test_transport", "test_filter", "test_ignore", "test_port", - "test_speed", "test_build_project_src", "debug_tool", "debug_port", - "debug_init_cmds", "debug_extra_cmds", "debug_server", - "debug_init_break", "debug_load_cmd", "debug_load_mode", - "debug_svd_path", "monitor_port", "monitor_speed", "monitor_rts", - "monitor_dtr" - ] - IGNORE_BUILD_OPTIONS = [ "test_transport", "test_filter", "test_ignore", "test_port", "test_speed", "debug_port", "debug_init_cmds", "debug_extra_cmds", @@ -157,17 +127,6 @@ class EnvironmentProcessor(object): REMAPED_OPTIONS = {"framework": "pioframework", "platform": "pioplatform"} - 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" - } - def __init__( self, # pylint: disable=R0913 cmd_ctx, @@ -201,7 +160,6 @@ class EnvironmentProcessor(object): self.name, fg="cyan", bold=True), "; ".join(env_dump))) click.secho("-" * terminal_width, bold=True) - self.options = self._validate_options(self.options) result = self._run() is_error = result['returncode'] != 0 @@ -218,31 +176,6 @@ class EnvironmentProcessor(object): return not is_error - def _validate_options(self, options): - result = {} - for k, v in options.items(): - # process obsolete options - if k in self.RENAMED_OPTIONS: - click.secho( - "Warning! `%s` option is deprecated and will be " - "removed in the next release! Please use " - "`%s` instead." % (k, self.RENAMED_OPTIONS[k]), - fg="yellow") - k = self.RENAMED_OPTIONS[k] - - # warn about unknown options - unknown_conditions = [ - k not in self.KNOWN_ENV_OPTIONS, not k.startswith("custom_"), - not k.startswith("board_") - ] - if all(unknown_conditions): - click.secho( - "Detected non-PlatformIO `%s` option in `[env:%s]` section" - % (k, self.name), - fg="yellow") - result[k] = v - return result - def get_build_variables(self): variables = {"pioenv": self.name} if self.upload_port: @@ -381,21 +314,7 @@ def print_summary(results, start_time): is_error=not successed) -def check_project_defopts(config): - if not config.has_section("platformio"): - return True - unknown = set(k for k, _ in config.items("platformio")) - set( - EnvironmentProcessor.KNOWN_PLATFORMIO_OPTIONS) - if not unknown: - return True - click.secho( - "Warning! Ignore unknown `%s` option in `[platformio]` section" % - ", ".join(unknown), - fg="yellow") - return False - - -def check_project_envs(config, environments=None): +def check_project_envs(config, environments=None): # FIXME: Remove if not config.sections(): raise exception.ProjectEnvsNotAvailable() diff --git a/platformio/exception.py b/platformio/exception.py index 92ffd6c5..26131a5d 100644 --- a/platformio/exception.py +++ b/platformio/exception.py @@ -42,11 +42,16 @@ class UserSideException(PlatformioException): pass -class AbortedByUser(PlatformioException): +class AbortedByUser(UserSideException): MESSAGE = "Aborted by user" +# +# Development Platform +# + + class UnknownPlatform(PlatformioException): MESSAGE = "Unknown development platform '{0}'" @@ -85,54 +90,75 @@ class UnknownFramework(PlatformioException): MESSAGE = "Unknown framework '{0}'" -class UnknownPackage(PlatformioException): +# Package Manager + + +class PlatformIOPackageException(PlatformioException): + pass + + +class UnknownPackage(PlatformIOPackageException): MESSAGE = "Detected unknown package '{0}'" -class MissingPackageManifest(PlatformioException): +class MissingPackageManifest(PlatformIOPackageException): MESSAGE = "Could not find one of '{0}' manifest files in the package" -class UndefinedPackageVersion(PlatformioException): +class UndefinedPackageVersion(PlatformIOPackageException): MESSAGE = ("Could not find a version that satisfies the requirement '{0}'" " for your system '{1}'") -class PackageInstallError(PlatformioException): +class PackageInstallError(PlatformIOPackageException): MESSAGE = ("Could not install '{0}' with version requirements '{1}' " "for your system '{2}'.\n\n" "Please try this solution -> http://bit.ly/faq-package-manager") -class ExtractArchiveItemError(PlatformioException): +class ExtractArchiveItemError(PlatformIOPackageException): MESSAGE = ( "Could not extract `{0}` to `{1}`. Try to disable antivirus " "tool or check this solution -> http://bit.ly/faq-package-manager") -class FDUnrecognizedStatusCode(PlatformioException): +class UnsupportedArchiveType(PlatformIOPackageException): + + MESSAGE = "Can not unpack file '{0}'" + + +class FDUnrecognizedStatusCode(PlatformIOPackageException): MESSAGE = "Got an unrecognized status code '{0}' when downloaded {1}" -class FDSizeMismatch(PlatformioException): +class FDSizeMismatch(PlatformIOPackageException): MESSAGE = ("The size ({0:d} bytes) of downloaded file '{1}' " "is not equal to remote size ({2:d} bytes)") -class FDSHASumMismatch(PlatformioException): +class FDSHASumMismatch(PlatformIOPackageException): MESSAGE = ("The 'sha1' sum '{0}' of downloaded file '{1}' " "is not equal to remote '{2}'") -class NotPlatformIOProject(PlatformioException): +# +# Project +# + + +class PlatformIOProjectException(PlatformioException): + pass + + +class NotPlatformIOProject(PlatformIOProjectException): MESSAGE = ( "Not a PlatformIO project. `platformio.ini` file has not been " @@ -140,26 +166,82 @@ class NotPlatformIOProject(PlatformioException): "please use `platformio init` command") -class UndefinedEnvPlatform(PlatformioException): +class InvalidProjectConf(PlatformIOProjectException): + + MESSAGE = ("Invalid '{0}' (project configuration file): '{1}'") + + +class UndefinedEnvPlatform(PlatformIOProjectException): MESSAGE = "Please specify platform for '{0}' environment" -class UnsupportedArchiveType(PlatformioException): - - MESSAGE = "Can not unpack file '{0}'" - - -class ProjectEnvsNotAvailable(PlatformioException): +class ProjectEnvsNotAvailable(PlatformIOProjectException): MESSAGE = "Please setup environments in `platformio.ini` file" -class UnknownEnvNames(PlatformioException): +class UnknownEnvNames(PlatformIOProjectException): # FIXME: UnknownProjectEnvs MESSAGE = "Unknown environment names '{0}'. Valid names are '{1}'" +# +# Library +# + + +class LibNotFound(PlatformioException): + + MESSAGE = ("Library `{0}` has not been found in PlatformIO Registry.\n" + "You can ignore this message, if `{0}` is a built-in library " + "(included in framework, SDK). E.g., SPI, Wire, etc.") + + +class NotGlobalLibDir(UserSideException): + + MESSAGE = ( + "The `{0}` is not a PlatformIO project.\n\n" + "To manage libraries in global storage `{1}`,\n" + "please use `platformio lib --global {2}` or specify custom storage " + "`platformio lib --storage-dir /path/to/storage/ {2}`.\n" + "Check `platformio lib --help` for details.") + + +class InvalidLibConfURL(PlatformioException): + + MESSAGE = "Invalid library config URL '{0}'" + + +# +# UDEV Rules +# + + +class InvalidUdevRules(PlatformioException): + pass + + +class MissedUdevRules(InvalidUdevRules): + + MESSAGE = ( + "Warning! Please install `99-platformio-udev.rules`. \nMode details: " + "https://docs.platformio.org/en/latest/faq.html#platformio-udev-rules") + + +class OutdatedUdevRules(InvalidUdevRules): + + MESSAGE = ( + "Warning! Your `{0}` are outdated. Please update or reinstall them." + "\n Mode details: https://docs.platformio.org" + "/en/latest/faq.html#platformio-udev-rules") + + +# +# Misc +# + + class GetSerialPortsError(PlatformioException): MESSAGE = "No implementation for your platform ('{0}') available" @@ -175,7 +257,7 @@ class APIRequestError(PlatformioException): MESSAGE = "[API] {0}" -class InternetIsOffline(PlatformioException): +class InternetIsOffline(UserSideException): MESSAGE = ( "You are not connected to the Internet.\n" @@ -183,33 +265,6 @@ class InternetIsOffline(PlatformioException): "to install all dependencies and toolchains.") -class LibNotFound(PlatformioException): - - MESSAGE = ("Library `{0}` has not been found in PlatformIO Registry.\n" - "You can ignore this message, if `{0}` is a built-in library " - "(included in framework, SDK). E.g., SPI, Wire, etc.") - - -class NotGlobalLibDir(PlatformioException): - - MESSAGE = ( - "The `{0}` is not a PlatformIO project.\n\n" - "To manage libraries in global storage `{1}`,\n" - "please use `platformio lib --global {2}` or specify custom storage " - "`platformio lib --storage-dir /path/to/storage/ {2}`.\n" - "Check `platformio lib --help` for details.") - - -class InvalidLibConfURL(PlatformioException): - - MESSAGE = "Invalid library config URL '{0}'" - - -class InvalidProjectConf(PlatformioException): - - MESSAGE = ("Invalid '{0}' (project configuration file): '{1}'") - - class BuildScriptNotFound(PlatformioException): MESSAGE = "Invalid path '{0}' to build script" @@ -237,25 +292,6 @@ class CIBuildEnvsEmpty(PlatformioException): "predefined environments using `--project-conf` option") -class InvalidUdevRules(PlatformioException): - pass - - -class MissedUdevRules(InvalidUdevRules): - - MESSAGE = ( - "Warning! Please install `99-platformio-udev.rules`. \nMode details: " - "https://docs.platformio.org/en/latest/faq.html#platformio-udev-rules") - - -class OutdatedUdevRules(InvalidUdevRules): - - MESSAGE = ( - "Warning! Your `{0}` are outdated. Please update or reinstall them." - "\n Mode details: https://docs.platformio.org" - "/en/latest/faq.html#platformio-udev-rules") - - class UpgradeError(PlatformioException): MESSAGE = """{0} @@ -290,5 +326,4 @@ class DebugSupportError(PlatformioException): class DebugInvalidOptions(PlatformioException): - pass diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index cd48ca61..26774a70 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -23,6 +23,7 @@ from click.testing import CliRunner from platformio import exception, util from platformio.commands.run import cli as cmd_run +from platformio.project.config import ProjectConfig class ProjectGenerator(object): @@ -44,14 +45,13 @@ class ProjectGenerator(object): @util.memoized() def get_project_env(self): data = {} - config = util.load_project_config(self.project_dir) - for section in config.sections(): - if not section.startswith("env:"): + config = ProjectConfig.get_instance( + join(self.project_dir, "platformio.ini")) + for env in config.envs(): + if self.env_name != env: continue - if self.env_name != section[4:]: - continue - data = {"env_name": section[4:]} - for k, v in config.items(section): + data = {"env_name": self.env_name} + for k, v in config.items(env=env): data[k] = v return data diff --git a/platformio/project/config.py b/platformio/project/config.py index 61e22473..f827020c 100644 --- a/platformio/project/config.py +++ b/platformio/project/config.py @@ -15,6 +15,7 @@ import glob import os import re +from os.path import isfile import click @@ -25,11 +26,99 @@ try: except ImportError: import configparser as ConfigParser +KNOWN_PLATFORMIO_OPTIONS = [ + "description", + "env_default", + "extra_configs", + + # Dirs + "home_dir", + "lib_dir", + "libdeps_dir", + "include_dir", + "src_dir", + "build_dir", + "data_dir", + "test_dir", + "boards_dir", + "lib_extra_dirs" +] + +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", + + # 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): VARTPL_RE = re.compile(r"\$\{([^\.\}]+)\.([^\}]+)\}") + _instances = {} _parser = None _parsed = [] @@ -49,13 +138,30 @@ class ProjectConfig(object): result.append(item) return result - def __init__(self, path): + @staticmethod + def get_instance(path): + if not isfile(path): + raise exception.NotPlatformIOProject(path) + if path not in ProjectConfig._instances: + ProjectConfig._instances[path] = ProjectConfig(path) + return ProjectConfig._instances[path] + + @staticmethod + def reset_instances(): + ProjectConfig._instances = {} + + def __init__(self, path, parse_extra=True): + if not isfile(path): + raise exception.NotPlatformIOProject(path) self.path = path self._parsed = [] self._parser = ConfigParser.ConfigParser() - self.read(path) + self.read(path, parse_extra) - def read(self, path): + def __getattr__(self, name): + return getattr(self._parser, name) + + def read(self, path, parse_extra=True): if path in self._parsed: return self._parsed.append(path) @@ -64,6 +170,9 @@ class ProjectConfig(object): except ConfigParser.Error as e: raise exception.InvalidProjectConf(path, str(e)) + if not parse_extra: + return + # load extra configs if (not self._parser.has_section("platformio") or not self._parser.has_option("platformio", "extra_configs")): @@ -74,14 +183,18 @@ class ProjectConfig(object): for item in glob.glob(pattern): self.read(item) - def __getattr__(self, name): - return getattr(self._parser, name) + def options(self, section=None, env=None): + assert section or env + if not section: + section = "env:" + env + return self._parser.options(section) - def items(self, section): - items = [] - for option in self._parser.options(section): - items.append((option, self.get(section, option))) - return items + def items(self, section=None, env=None): + assert section or env + if not section: + section = "env:" + env + return [(option, self.get(section, option)) + for option in self.options(section)] def get(self, section, option): try: @@ -95,7 +208,7 @@ class ProjectConfig(object): def _re_sub_handler(self, match): section, option = match.group(1), match.group(2) if section in ("env", - "sysenv") and not self.has_section(section): + "sysenv") and not self._parser.has_section(section): if section == "env": click.secho( "Warning! Access to system environment variable via " @@ -104,3 +217,67 @@ class ProjectConfig(object): fg="yellow") return os.getenv(option) return self.get(section, option) + + def envs(self): + return [s[4:] for s in self._parser.sections() if s.startswith("env:")] + + def default_envs(self): + if not self._parser.has_option("platformio", "env_default"): + return [] + return self.parse_multi_values(self.get("platformio", "env_default")) + + def validate(self, envs=None): + # check envs + known = set(self.envs()) + if not known: + raise exception.ProjectEnvsNotAvailable() + + unknown = set((envs or []) + self.default_envs()) - known + if unknown: + raise exception.UnknownEnvNames(", ".join(unknown), + ", ".join(known)) + return self.validate_options() + + def validate_options(self): + warnings = set() + # check [platformio] section + if self._parser.has_section("platformio"): + unknown = set(k for k, _ in self.items("platformio")) - set( + KNOWN_PLATFORMIO_OPTIONS) + if unknown: + warnings.add( + "Ignore unknown `%s` options in `[platformio]` section" % + ", ".join(unknown)) + + # check [env:*] sections + for section in self._parser.sections(): + if not section.startswith("env:"): + continue + for option in self._parser.options(section): + # obsolete + if option in RENAMED_OPTIONS: + warnings.add( + "`%s` option in `[%s]` section is deprecated and will " + "be removed in the next release! Please use `%s` " + "instead" % (option, section, RENAMED_OPTIONS[option])) + # rename on-the-fly + self._parser.set(section, RENAMED_OPTIONS[option], + self._parser.get(section, option)) + self._parser.remove_option(section, option) + continue + + # unknown + unknown_conditions = [ + option not in KNOWN_ENV_OPTIONS, + not option.startswith("custom_"), + not option.startswith("board_") + ] # yapf: disable + if all(unknown_conditions): + warnings.add( + "Detected non-PlatformIO `%s` option in `[%s]` section" + % (option, section)) + + for warning in warnings: + click.secho("Warning! %s" % warning, fg="yellow") + + return True diff --git a/platformio/telemetry.py b/platformio/telemetry.py index 3f9937cf..12e4a034 100644 --- a/platformio/telemetry.py +++ b/platformio/telemetry.py @@ -342,12 +342,9 @@ def on_exception(e): return text.strip() skip_conditions = [ - isinstance(e, cls) - for cls in (IOError, exception.ReturnErrorCode, - exception.AbortedByUser, exception.NotGlobalLibDir, - exception.InternetIsOffline, - exception.NotPlatformIOProject, - exception.UserSideException) + isinstance(e, cls) for cls in (IOError, exception.ReturnErrorCode, + exception.UserSideException, + exception.PlatformIOProjectException) ] try: skip_conditions.append("[API] Account: " in str(e)) diff --git a/platformio/util.py b/platformio/util.py index 1adcd936..85a55dc6 100644 --- a/platformio/util.py +++ b/platformio/util.py @@ -180,7 +180,8 @@ def get_project_optional_dir(name, default=None): paths = os.getenv(var_name) else: try: - config = load_project_config() + 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) @@ -312,7 +313,7 @@ def get_projectdata_dir(): "data")) -def load_project_config(path=None): # FIXME: +def load_project_config(path=None): # FIXME: Remove if not path or isdir(path): path = join(path or get_project_dir(), "platformio.ini") if not isfile(path): @@ -321,7 +322,7 @@ def load_project_config(path=None): # FIXME: return ProjectConfig(path) -def parse_conf_multi_values(items): # FIXME: +def parse_conf_multi_values(items): # FIXME: Remove return ProjectConfig.parse_multi_values(items) diff --git a/tests/conftest.py b/tests/conftest.py index 02d16069..d53d92e8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,8 +24,10 @@ from platformio import util def validate_cliresult(): def decorator(result): - assert result.exit_code == 0, result.output - assert not result.exception, result.output + assert result.exit_code == 0, "{} => {}".format( + result.exception, result.output) + assert not result.exception, "{} => {}".format(result.exception, + result.output) return decorator diff --git a/tests/test_projectconf.py b/tests/test_projectconf.py index f47ae9af..f6228b88 100644 --- a/tests/test_projectconf.py +++ b/tests/test_projectconf.py @@ -13,10 +13,12 @@ # limitations under the License. import os + from platformio.project.config import ProjectConfig BASE_CONFIG = """ [platformio] +env_default = esp32dev, lolin32 extra_configs = extra_envs.ini extra_debug.ini @@ -86,8 +88,15 @@ def test_parser(tmpdir): "build_flags") == ("-D DEBUG=1 -L /usr/local/lib") # items - assert config.items("env:esp32dev") == [("platform", "espressif32"), + assert config.items("common") == [("debug_flags", "-D DEBUG=1"), + ("lib_flags", "-lc -lm"), + ("extra_flags", "-L /usr/local/lib")] + assert config.items(env="esp32dev") == [("platform", "espressif32"), ("framework", "espidf"), ("board", "esp32dev"), ("build_flags", "-lc -lm -D DEBUG=1")] + + # envs + assert config.envs() == ["esp-wrover-kit", "esp32dev", "lolin32"] + assert config.default_envs() == ["esp32dev", "lolin32"]