diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index d1efd097..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -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" -} diff --git a/Makefile b/Makefile index 6df42c42..bc8ba602 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ yapf: yapf --recursive --in-place platformio/ 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 diff --git a/platformio/commands/device.py b/platformio/commands/device.py index c5bde2bd..594020eb 100644 --- a/platformio/commands/device.py +++ b/platformio/commands/device.py @@ -163,22 +163,17 @@ def device_list( # pylint: disable=too-many-branches "--environment", help="Load configuration from `platformio.ini` and specified environment") def device_monitor(**kwargs): # pylint: disable=too-many-branches - custom_monitor_flags = [] try: env_options = get_project_options(kwargs['project_dir'], kwargs['environment']) - if "monitor_flags" in env_options: - custom_monitor_flags = ProjectConfig.parse_multi_values( - env_options['monitor_flags']) - if env_options: - for k in ("port", "speed", "rts", "dtr"): - k2 = "monitor_%s" % k - if k == "speed": - k = "baud" - if kwargs[k] is None and k2 in env_options: - kwargs[k] = env_options[k2] - if k != "port": - kwargs[k] = int(kwargs[k]) + for k in ("port", "speed", "rts", "dtr"): + k2 = "monitor_%s" % k + if k == "speed": + k = "baud" + 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: pass @@ -187,12 +182,12 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches if len(ports) == 1: 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(): if k in ("port", "baud", "rts", "dtr", "environment", "project_dir"): continue k = "--" + k.replace("_", "-") - if k in custom_monitor_flags: + if k in env_options.get("monitor_flags", []): continue if isinstance(v, bool): if v: diff --git a/platformio/commands/home/rpc/handlers/project.py b/platformio/commands/home/rpc/handlers/project.py index aee042e2..73bf69a3 100644 --- a/platformio/commands/home/rpc/handlers/project.py +++ b/platformio/commands/home/rpc/handlers/project.py @@ -44,10 +44,8 @@ class ProjectRPC(object): config.validate(validate_options=False) libdeps_dir = get_project_libdeps_dir() - if config.has_section("platformio") and \ - config.has_option("platformio", "lib_extra_dirs"): - data['libExtraDirs'].extend( - config.getlist("platformio", "lib_extra_dirs")) + data['libExtraDirs'].extend( + config.get("platformio", "lib_extra_dirs", [])) for section in config.sections(): if not section.startswith("env:"): @@ -55,9 +53,8 @@ class ProjectRPC(object): data['envLibdepsDirs'].append(join(libdeps_dir, section[4:])) if config.has_option(section, "board"): data['boards'].append(config.get(section, "board")) - if config.has_option(section, "lib_extra_dirs"): - data['libExtraDirs'].extend( - config.getlist(section, "lib_extra_dirs")) + data['libExtraDirs'].extend( + config.get(section, "lib_extra_dirs", [])) # skip non existing folders and resolve full path for key in ("envLibdepsDirs", "libExtraDirs"): @@ -233,10 +230,8 @@ class ProjectRPC(object): try: config = ProjectConfig(join(project_dir, "platformio.ini")) config.validate(validate_options=False) - if config.has_section("platformio") and \ - config.has_option("platformio", "description"): - project_description = config.get( - "platformio", "description") + project_description = config.get("platformio", + "description") except exception.PlatformIOProjectException: continue diff --git a/platformio/commands/init.py b/platformio/commands/init.py index 7232a5ef..35e459bf 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -70,7 +70,6 @@ def cli( project_option, env_prefix, silent): - if not silent: if project_dir == getcwd(): click.secho( diff --git a/platformio/commands/lib.py b/platformio/commands/lib.py index ae00902f..63b89950 100644 --- a/platformio/commands/lib.py +++ b/platformio/commands/lib.py @@ -109,9 +109,8 @@ def cli(ctx, **options): continue storage_dir = join(libdeps_dir, env) 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.getlist("env:" + env, "lib_deps") + ctx.meta[CTX_META_STORAGE_LIBDEPS_KEY][storage_dir] = config.get( + "env:" + env, "lib_deps", []) @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: continue config.expand_interpolations = False - lib_deps = (config.getlist("env:" + env, "lib_deps") - if config.has_option("env:" + env, "lib_deps") else []) + lib_deps = config.get("env:" + env, "lib_deps", []) for library in libraries: if library in lib_deps: continue diff --git a/platformio/commands/test/command.py b/platformio/commands/test/command.py index 5f41649b..e69fc953 100644 --- a/platformio/commands/test/command.py +++ b/platformio/commands/test/command.py @@ -107,9 +107,8 @@ def cli( # pylint: disable=redefined-builtin # filter and ignore patterns patterns = dict(filter=list(filter), ignore=list(ignore)) for key in patterns: - if config.has_option(section, "test_%s" % key): - patterns[key].extend( - config.getlist(section, "test_%s" % key)) + patterns[key].extend( + config.get(section, "test_%s" % key, [])) skip_conditions = [ environment and envname not in environment, diff --git a/platformio/commands/test/processor.py b/platformio/commands/test/processor.py index e5a4e32d..f116d34e 100644 --- a/platformio/commands/test/processor.py +++ b/platformio/commands/test/processor.py @@ -92,9 +92,10 @@ class TestProcessorBase(object): self._outputcpp_generated = False def get_transport(self): - transport = self.env_options.get("framework") if self.env_options.get("platform") == "native": transport = "native" + elif "framework" in self.env_options: + transport = self.env_options.get("framework")[0] if "test_transport" in self.env_options: transport = self.env_options['test_transport'] if transport not in TRANSPORT_OPTIONS: diff --git a/platformio/project/config.py b/platformio/project/config.py index d67df78b..a1b3b428 100644 --- a/platformio/project/config.py +++ b/platformio/project/config.py @@ -21,6 +21,7 @@ from os.path import isfile import click from platformio import exception +from platformio.project.options import ProjectOptions try: 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): @@ -191,11 +100,7 @@ class ProjectConfig(object): return # load extra configs - if (not self._parser.has_section("platformio") - or not self._parser.has_option("platformio", "extra_configs")): - return - extra_configs = self.getlist("platformio", "extra_configs") - for pattern in extra_configs: + for pattern in self.get("platformio", "extra_configs", []): for item in glob.glob(pattern): self.read(item) @@ -212,6 +117,14 @@ class ProjectConfig(object): if option not in options: 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 def has_option(self, section, option): @@ -239,37 +152,64 @@ class ProjectConfig(object): value = "\n" + value # start from a new line self._parser.set(section, option, value) - def get(self, section, option): + def getraw(self, section, option): if not self.expand_interpolations: return self._parser.get(section, option) try: 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) - except ConfigParser.Error as e: - raise exception.InvalidProjectConf(self.path, str(e)) if "${" not in value or "}" not in 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) if section == "sysenv": return os.getenv(option) - return self.get(section, option) + return self.getraw(section, option) - def getlist(self, section, option): - return self.parse_multi_values(self.get(section, option)) + def get(self, section, option, default=None): + value = default + 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 + + 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 + + return value 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.getlist("platformio", "env_default") + return self.get("platformio", "env_default", []) def validate(self, envs=None, validate_options=True): if not isfile(self.path): @@ -286,66 +226,54 @@ class ProjectConfig(object): return self.validate_options() if validate_options else True def validate_options(self): - return (self._validate_platformio_options() - and self._validate_env_options()) - - def _validate_platformio_options(self): - if not self._parser.has_section("platformio"): - return True - warnings = set() - - # legacy `lib_extra_dirs` - if self._parser.has_option("platformio", "lib_extra_dirs"): + # legacy `lib_extra_dirs` in [platformio] + if (self._parser.has_section("platformio") + and self._parser.has_option("platformio", "lib_extra_dirs")): if not self._parser.has_section("env"): self._parser.add_section("env") self._parser.set("env", "lib_extra_dirs", self._parser.get("platformio", "lib_extra_dirs")) self._parser.remove_option("platformio", "lib_extra_dirs") - warnings.add( - "`lib_extra_dirs` option is deprecated in section " - "`platformio`! Please move it to global `env` section") + click.secho( + "Warning! `lib_extra_dirs` option is deprecated in section " + "[platformio]! Please move it to global `env` section", + fg="yellow") - unknown = set(k for k, _ in self.items("platformio")) - set( - KNOWN_PLATFORMIO_OPTIONS) - if unknown: - warnings.add( - "Ignore unknown `%s` options in section `[platformio]`" % - ", ".join(unknown)) + return self._validate_unknown_options() - for warning in warnings: - click.secho("Warning! %s" % warning, fg="yellow") - - return True - - def _validate_env_options(self): + def _validate_unknown_options(self): warnings = set() + 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(): - if section != "env" and not section.startswith("env:"): - continue for option in self._parser.options(section): # obsolete - if option in RENAMED_OPTIONS: + if option in renamed_options: warnings.add( "`%s` option in section `[%s]` is deprecated and will " "be removed in the next release! Please use `%s` " - "instead" % (option, section, RENAMED_OPTIONS[option])) + "instead" % (option, section, renamed_options[option])) # 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.remove_option(section, option) continue # unknown + scope = section.split(":", 1)[0] unknown_conditions = [ - option not in KNOWN_ENV_OPTIONS, - not option.startswith("custom_"), - not option.startswith("board_") + ("%s.%s" % (scope, option)) not in ProjectOptions, + scope != "env" or + not option.startswith(("custom_", "board_")) ] # yapf: disable if all(unknown_conditions): - warnings.add( - "Detected non-PlatformIO `%s` option in `[%s]` section" - % (option, section)) + warnings.add("Ignore unknown option `%s` in section `[%s]`" + % (option, section)) for warning in warnings: click.secho("Warning! %s" % warning, fg="yellow") diff --git a/platformio/project/helpers.py b/platformio/project/helpers.py index a2acce4d..2746c4d7 100644 --- a/platformio/project/helpers.py +++ b/platformio/project/helpers.py @@ -15,7 +15,7 @@ import os from hashlib import sha1 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) from platformio import __version__ @@ -44,37 +44,23 @@ def find_project_dir_above(path): 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 - 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: + if not optional_dir: return default - items = [] - for item in paths.split(", "): - 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( + if "$PROJECT_HASH" in optional_dir: + optional_dir = optional_dir.replace( "$PROJECT_HASH", sha1(project_dir if PY2 else project_dir.encode()).hexdigest() [:10]) - return paths + if optional_dir.startswith("~"): + optional_dir = expanduser(optional_dir) + + return realpath(optional_dir) def get_project_core_dir(): diff --git a/platformio/project/options.py b/platformio/project/options.py new file mode 100644 index 00000000..7c68a884 --- /dev/null +++ b/platformio/project/options.py @@ -0,0 +1,199 @@ +# Copyright (c) 2014-present PlatformIO +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# 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"], type=int), + 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), # FIXME: B + + # 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") + ] +]) diff --git a/tests/commands/test_ci.py b/tests/commands/test_ci.py index 67ff4420..bce43700 100644 --- a/tests/commands/test_ci.py +++ b/tests/commands/test_ci.py @@ -20,7 +20,7 @@ from platformio.commands.lib import cli as cmd_lib def test_ci_empty(clirunner): result = clirunner.invoke(cmd_ci) - assert result.exit_code == 2 + assert result.exit_code != 0 assert "Invalid value: Missing argument 'src'" in result.output diff --git a/tests/commands/test_init.py b/tests/commands/test_init.py index 800a0b47..e8ad5547 100644 --- a/tests/commands/test_init.py +++ b/tests/commands/test_init.py @@ -16,7 +16,7 @@ import json from os import getcwd, makedirs 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.init import cli as cmd_init from platformio.project.config import ProjectConfig @@ -109,13 +109,12 @@ def test_init_special_board(clirunner, validate_cliresult): config = ProjectConfig(join(getcwd(), "platformio.ini")) config.validate() - expected_result = [("platform", str(boards[0]['platform'])), - ("framework", - str(boards[0]['frameworks'][0])), ("board", "uno")] + expected_result = [("platform", boards[0]['platform']), + ("board", "uno"), + ("framework", [boards[0]['frameworks'][0]])] assert config.has_section("env:uno") - assert not set(expected_result).symmetric_difference( - set(config.items("env:uno"))) + assert config.items("env:uno") == expected_result def test_init_enable_auto_uploading(clirunner, validate_cliresult): @@ -126,11 +125,10 @@ def test_init_enable_auto_uploading(clirunner, validate_cliresult): validate_pioproject(getcwd()) config = ProjectConfig(join(getcwd(), "platformio.ini")) config.validate() - expected_result = [("platform", "atmelavr"), ("framework", "arduino"), - ("board", "uno"), ("targets", "upload")] + expected_result = [("targets", ["upload"]), ("platform", "atmelavr"), + ("board", "uno"), ("framework", ["arduino"])] assert config.has_section("env:uno") - assert not set(expected_result).symmetric_difference( - set(config.items("env:uno"))) + assert config.items("env:uno") == expected_result def test_init_custom_framework(clirunner, validate_cliresult): @@ -141,11 +139,10 @@ def test_init_custom_framework(clirunner, validate_cliresult): validate_pioproject(getcwd()) config = ProjectConfig(join(getcwd(), "platformio.ini")) config.validate() - expected_result = [("platform", "teensy"), ("framework", "mbed"), - ("board", "teensy31")] + expected_result = [("platform", "teensy"), ("board", "teensy31"), + ("framework", ["mbed"])] assert config.has_section("env:teensy31") - assert not set(expected_result).symmetric_difference( - set(config.items("env:teensy31"))) + assert config.items("env:teensy31") == expected_result def test_init_incorrect_board(clirunner): diff --git a/tests/test_projectconf.py b/tests/test_projectconf.py index d58ee0ce..5f10b305 100644 --- a/tests/test_projectconf.py +++ b/tests/test_projectconf.py @@ -14,7 +14,9 @@ import os -from platformio.project.config import ProjectConfig +import pytest + +from platformio.project.config import ConfigParser, ProjectConfig BASE_CONFIG = """ [platformio] @@ -26,7 +28,9 @@ extra_configs = # global options per [env:*] [env] monitor_speed = 115200 -lib_deps = Lib1, Lib2 +lib_deps = + Lib1 + Lib2 lib_ignore = ${custom.lib_ignore} [custom] @@ -46,6 +50,7 @@ build_flags = ${custom.lib_flags} ${custom.debug_flags} [env:extra_2] build_flags = ${custom.debug_flags} ${custom.extra_flags} lib_ignore = ${env.lib_ignore}, Lib3 +upload_port = /dev/extra_2/port """ 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("extra_envs.ini").write(EXTRA_ENVS_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) 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 assert config.sections() == [ "platformio", "env", "custom", "env:base", "env:extra_1", "env:extra_2" @@ -88,29 +103,75 @@ def test_parser(tmpdir): # sysenv assert config.get("custom", "extra_flags") == "" + 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" 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 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_2", "build_flags") == "-Og" + assert config.get("env:extra_1", "build_flags") == [ + "-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:base", - "build_flags") == ("-D DEBUG=1 -L /usr/local/lib") + assert config.get("env:base", "build_flags") == ([ + "-D DEBUG=1 -L /usr/local/lib", "-DSYSENVDEPS1 -DSYSENVDEPS2" + ]) # items - assert config.items("custom") == [("debug_flags", "-D DEBUG=1"), - ("lib_flags", "-lc -lm"), - ("extra_flags", "-L /usr/local/lib"), - ("lib_ignore", "LibIgnoreCustom")] - assert config.items(env="extra_1") == [("build_flags", - "-lc -lm -D DEBUG=1"), - ("monitor_speed", "115200"), - ("lib_deps", "Lib1, Lib2"), - ("lib_ignore", "LibIgnoreCustom")] - assert config.items(env="extra_2") == [("build_flags", "-Og"), - ("lib_ignore", - "LibIgnoreCustom, Lib3"), - ("monitor_speed", "115200"), - ("lib_deps", "Lib1, Lib2")] + assert config.items("custom") == [ + ("debug_flags", "-D DEBUG=1"), + ("lib_flags", "-lc -lm"), + ("extra_flags", "-L /usr/local/lib"), + ("lib_ignore", "LibIgnoreCustom") + ] # yapf: disable + assert config.items(env="extra_1") == [ + ("build_flags", ["-lc -lm -D DEBUG=1", "-DSYSENVDEPS1 -DSYSENVDEPS2"]), + ("monitor_speed", "115200"), + ("lib_deps", ["Lib1", "Lib2"]), + ("lib_ignore", ["LibIgnoreCustom"]), + ("upload_port", "/dev/sysenv/port") + ] # yapf: disable + 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_CORE_DIR"] = "/custom/core/dir" + assert config.get("platformio", "core_dir") == "/custom/core/dir" + del os.environ["PLATFORMIO_CORE_DIR"]