Dynamic variables/templates for "platformio.ini" // Resolve #705

This commit is contained in:
Ivan Kravets
2016-09-17 23:46:53 +03:00
parent c12df19fd1
commit 5e6469596c
7 changed files with 117 additions and 33 deletions

View File

@ -7,6 +7,8 @@ PlatformIO 3.0
3.1.0 (2016-09-??)
~~~~~~~~~~~~~~~~~~
* New! Dynamic variables/templates for `Project Configuration File "platformio.ini" <http://docs.platformio.org/en/stable/projectconf.html>`__
(`issue #705 <https://github.com/platformio/platformio/issues/705>`_)
* Implemented LocalCache system for API and improved a work in off-line mode
* Improved Project Generator when custom ``--project-option`` is passed to
`platformio init <http://docs.platformio.org/en/stable/userguide/cmd_init.html>`__

View File

@ -21,13 +21,73 @@ The Project configuration file is named ``platformio.ini``. This is a
key / value pairs within the sections. Lines beginning with ``;``
are ignored and may be used to provide comments.
The sections and their allowable values are described below.
There are 2 system reserved sections:
* Base PlatformIO settings: :ref:`projectconf_section_platformio`
* Build Environment settings: :ref:`projectconf_section_env`
The other sections can be used by users, for example, for
:ref:`projectconf_dynamic_vars`. The sections and their allowable values are
described below.
.. contents::
:depth: 2
.. _projectconf_dynamic_vars:
Dynamic variables
-----------------
.. versionadded:: 3.1
Dynamic variables/templates are useful when you have common configuration data
between build environments. For examples, common :ref:`projectconf_build_flags`
or project dependencies :ref:`projectconf_lib_deps`.
Each variable should have a next format: ``${<section>.<option>}``, where
``<section>`` is a value from ``[<section>]`` group, and ``<option>`` is a
first item from pair ``<option> = value``.
* Variable can be applied only for the option's value
* Multiple variables are allowed
* The ``platformio`` section is reserved and could not be used as custom
section. Some good section names might be ``common`` or ``global``.
Example:
.. code-block:: ini
[common]
build_flags = -D VERSION=1.2.3 -D DEBUG=1
lib_deps_builtin = SPI, Wire
lib_deps_external = ArduinoJson@>5.6.0
[env:uno]
platform = atmelavr
framework = arduino
board = uno
build_flags = ${common.build_flags}
lib_deps = ${common.lib_deps_builtin}, ${common.lib_deps_external}
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
build_flags = ${common.build_flags} -DSSID_NAME=HELLO -DSSID_PASWORD=WORLD
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
PubSubClient@2.6
OneWire
.. _projectconf_section_platformio:
Section ``[platformio]``
------------------------
.. contents::
:local:
A ``platformio`` section is used for overriding default configuration options
.. note::
@ -45,7 +105,8 @@ Options
^^^^^^^^^^^^
Is used to store platform toolchains, frameworks, global libraries for
:ref: `ldf`, service data and etc.
:ref: `ldf`, service data and etc. The size of this folder will depend on
number of installed development platforms.
A default value is User's home directory:
@ -55,6 +116,13 @@ A default value is User's home directory:
This option can be overridden by global environment variable
:envvar:`PLATFORMIO_HOME_DIR`.
Example:
.. code-block:: ini
[platformio]
home_dir = /path/to/custom/pio/storage
.. _projectconf_pio_lib_dir:
``lib_dir``
@ -223,11 +291,14 @@ Multiple environments are allowed if they are separated with ", "
board = lpmsp430g2553
build_flags = -D LED_BUILTIN=RED_LED
----------
.. _projectconf_section_env:
Section ``[env:NAME]``
----------------------
.. contents::
:local:
A section with ``env:`` prefix is used to define virtual environment with
specific options that will be processed with :ref:`cmd_run` command. You can
define unlimited numbers of environments.
@ -703,6 +774,9 @@ Specify project dependencies that should be installed automatically to
:ref:`projectconf_pio_libdeps_dir` before environment processing.
Multiple dependencies are allowed (multi-lines or separated with comma+space ", ").
If you have multiple build environments that depend on the same libraries,
you can use :ref:`projectconf_dynamic_vars` to use common configuration.
**Valid forms**
.. code-block:: ini

View File

@ -14,7 +14,7 @@
import sys
VERSION = (3, 1, "0a3")
VERSION = (3, 1, "0a4")
__version__ = ".".join([str(s) for s in VERSION])
__title__ = "platformio"

View File

@ -26,12 +26,6 @@ from platformio.commands.init import validate_boards
from platformio.commands.run import cli as cmd_run
from platformio.exception import CIBuildEnvsEmpty
# pylint: disable=wrong-import-order
try:
from configparser import ConfigParser
except ImportError:
from ConfigParser import ConfigParser
def validate_path(ctx, param, value): # pylint: disable=unused-argument
invalid_path = None
@ -175,9 +169,8 @@ def _exclude_contents(dst_dir, patterns):
def _copy_project_conf(build_dir, project_conf):
cp = ConfigParser()
cp.read(project_conf)
if cp.has_section("platformio"):
cp.remove_section("platformio")
config = util.load_project_config(project_conf)
if config.has_section("platformio"):
config.remove_section("platformio")
with open(join(build_dir, "platformio.ini"), "w") as fp:
cp.write(fp)
config.write(fp)

View File

@ -82,12 +82,8 @@ def cli(ctx, environment, target, upload_port, project_dir, silent, verbose,
results = []
for section in config.sections():
# skip main configuration section
if section == "platformio":
continue
if not section.startswith("env:"):
raise exception.InvalidEnvName(section)
continue
envname = section[4:]
skipenv = any([environment and envname not in environment,

View File

@ -131,11 +131,6 @@ class ProjectEnvsNotAvailable(PlatformioException):
MESSAGE = "Please setup environments in `platformio.ini` file"
class InvalidEnvName(PlatformioException):
MESSAGE = "Invalid environment '{0}'. The name must start with 'env:'"
class UnknownEnvNames(PlatformioException):
MESSAGE = "Unknown environment names '{0}'. Valid names are '{1}'"

View File

@ -33,13 +33,35 @@ import requests
from platformio import __apiurl__, __version__, exception
# pylint: disable=wrong-import-order
# pylint: disable=wrong-import-order, too-many-ancestors
try:
from configparser import ConfigParser
except ImportError:
from ConfigParser import ConfigParser
class ProjectConfig(ConfigParser):
VARTPL_RE = re.compile(r"\$\{([^\.\}]+)\.([^\}]+)\}")
def items(self, section, **_):
ConfigParser.items()
items = []
for option in ConfigParser.options(self, section):
items.append((option, self.get(section, option)))
return items
def get(self, section, option, **kwargs):
value = ConfigParser.get(self, section, option, **kwargs)
if "${" not in value or "}" not in value:
return value
return self.VARTPL_RE.sub(self._re_sub_handler, value)
def _re_sub_handler(self, match):
return self.get(match.group(1), match.group(2))
class AsyncPipe(Thread):
def __init__(self, outcallback=None):
@ -256,13 +278,15 @@ def get_projectdata_dir():
join(get_project_dir(), "data"))
def load_project_config(project_dir=None):
if not project_dir:
project_dir = get_project_dir()
if not is_platformio_project(project_dir):
raise exception.NotPlatformIOProject(project_dir)
cp = ConfigParser()
cp.read(join(project_dir, "platformio.ini"))
def load_project_config(path=None):
if not path or isdir(path):
project_dir = path or get_project_dir()
if not is_platformio_project(project_dir):
raise exception.NotPlatformIOProject(project_dir)
path = join(project_dir, "platformio.ini")
assert isfile(path)
cp = ProjectConfig()
cp.read(path)
return cp