Merge branch 'hotfix/v3.5.4'

This commit is contained in:
Ivan Kravets
2018-07-03 15:11:32 +03:00
21 changed files with 264 additions and 143 deletions

View File

@ -4,6 +4,30 @@ Release Notes
PlatformIO 3.0
--------------
3.5.4 (2018-07-03)
~~~~~~~~~~~~~~~~~~
* Improved removing of default build flags using `build_unflags <http://docs.platformio.org/page/projectconf/section_env_build.html#build-unflags>`__ option
(`issue #1712 <https://github.com/platformio/platformio-core/issues/1712>`_)
* Export ``LIBS``, ``LIBPATH``, and ``LINKFLAGS`` data from project dependent
libraries to the global build environment
* Don't export ``CPPPATH`` data of project dependent libraries to framework's
build environment
(`issue #1665 <https://github.com/platformio/platformio-core/issues/1665>`_)
* Handle "architectures" data from "library.properties" manifest in
`lib_compat_mode = strict <http://docs.platformio.org/en/page/librarymanager/ldf.html#compatibility-mode>`__
* Added workaround for Python SemVer package's `issue #61 <https://github.com/rbarrois/python-semanticversion/issues/61>`_ with caret range and pre-releases
* Replaced conflicted "env" pattern by "sysenv" for `"platformio.ini" Dynamic Variables" <http://docs.platformio.org/page/projectconf/dynamic_variables.html>`__
(`issue #1705 <https://github.com/platformio/platformio-core/issues/1705>`_)
* Removed "date&time" when processing project with `platformio run <http://docs.platformio.org/page/userguide/cmd_run.html>`__ command
(`issue #1343 <https://github.com/platformio/platformio-core/issues/1343>`_)
* Fixed issue with invalid LD script if path contains space
* Fixed preprocessor for Arduino sketch when function returns certain type
(`issue #1683 <https://github.com/platformio/platformio-core/issues/1683>`_)
* Fixed issue when `platformio lib uninstall <http://docs.platformio.org/page/userguide/lib/cmd_uninstall.html>`__
removes initial source code
(`issue #1023 <https://github.com/platformio/platformio-core/issues/1023>`_)
3.5.3 (2018-06-01)
~~~~~~~~~~~~~~~~~~
@ -19,7 +43,7 @@ PlatformIO 3.0
(`issue #1612 <https://github.com/platformio/platformio-core/issues/1612>`_)
* Configure a custom path to SVD file using `debug_svd_path <http://docs.platformio.org/page/projectconf/section_env_debug.html#debug-svd-path>`__
option
* Custom project `description <http://docs.platformio.org/en/latest/projectconf/section_platformio.html#description>`_
* Custom project `description <http://docs.platformio.org/en/page/projectconf/section_platformio.html#description>`_
which will be used by `PlatformIO Home <http://docs.platformio.org/page/home/index.html>`_
* Updated Unity tool to 2.4.3
* Improved support for Black Magic Probe in "uploader" mode
@ -45,9 +69,9 @@ PlatformIO 3.0
- Multiple themes (Dark & Light)
- Ability to specify a name for new project
* Control `PIO Unified Debugger <http://docs.platformio.org/en/latest/plus/debugging.html>`__
* Control `PIO Unified Debugger <http://docs.platformio.org/en/page/plus/debugging.html>`__
and its firmware loading mode using
`debug_load_mode <http://docs.platformio.org/en/latest/projectconf/section_env_debug.html#debug-load-mode>`__ option
`debug_load_mode <http://docs.platformio.org/en/page/projectconf/section_env_debug.html#debug-load-mode>`__ option
* Added aliases (off, light, strict) for
`LDF Compatibility Mode <http://docs.platformio.org/page/librarymanager/ldf.html>`__
* Search for a library using PIO Library Registry ID ``id:X`` (e.g. ``pio lib search id:13``)

2
docs

Submodule docs updated: 3ad76be8f7...0b8ac5fbf7

View File

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

View File

@ -279,9 +279,8 @@ class LibBuilderBase(object):
if (key in item and
not util.items_in_list(self.env[env_key], item[key])):
if self.verbose:
sys.stderr.write(
"Skip %s incompatible dependency %s\n" % (key[:-1],
item))
sys.stderr.write("Skip %s incompatible dependency %s\n"
% (key[:-1], item))
skip = True
if skip:
continue
@ -394,9 +393,9 @@ class LibBuilderBase(object):
if self != lb:
if _already_depends(lb):
if self.verbose:
sys.stderr.write("Warning! Circular dependencies detected "
"between `%s` and `%s`\n" % (self.path,
lb.path))
sys.stderr.write(
"Warning! Circular dependencies detected "
"between `%s` and `%s`\n" % (self.path, lb.path))
self._circular_deps.append(lb)
elif lb not in self._depbuilders:
self._depbuilders.append(lb)
@ -502,6 +501,28 @@ class ArduinoLibBuilder(LibBuilderBase):
def is_frameworks_compatible(self, frameworks):
return util.items_in_list(frameworks, ["arduino", "energia"])
def is_platforms_compatible(self, platforms):
platforms_map = {
"avr": "atmelavr",
"sam": "atmelsam",
"samd": "atmelsam",
"esp8266": "espressif8266",
"esp32": "espressif32",
"arc32": "intel_arc32",
"stm32": "ststm32"
}
items = []
for arch in self._manifest.get("architectures", "").split(","):
arch = arch.strip()
if arch == "*":
items = "*"
break
if arch in platforms_map:
items.append(platforms_map[arch])
if not items:
return LibBuilderBase.is_platforms_compatible(self, platforms)
return util.items_in_list(platforms, items)
class MbedLibBuilder(LibBuilderBase):
@ -803,7 +824,7 @@ def GetLibBuilders(env): # pylint: disable=too-many-branches
return items
def BuildProjectLibraries(env):
def ConfigureProjectLibBuilder(env):
def correct_found_libs(lib_builders):
# build full dependency graph
@ -822,7 +843,7 @@ def BuildProjectLibraries(env):
title = "<%s>" % lb.name
vcs_info = lb.vcs_info
if lb.version:
title += " v%s" % lb.version
title += " %s" % lb.version
if vcs_info and vcs_info.get("version"):
title += " #%s" % vcs_info.get("version")
sys.stdout.write("%s|-- %s" % (margin, title))
@ -837,7 +858,6 @@ def BuildProjectLibraries(env):
print_deps_tree(lb, level + 1)
project = ProjectAsLibBuilder(env, "$PROJECT_DIR")
project.env = env
ldf_mode = LibBuilderBase.lib_ldf_mode.fget(project)
print "Library Dependency Finder -> http://bit.ly/configure-pio-ldf"
@ -859,7 +879,7 @@ def BuildProjectLibraries(env):
else:
print "No dependencies"
return project.build()
return project
def exists(_):
@ -868,5 +888,5 @@ def exists(_):
def generate(env):
env.AddMethod(GetLibBuilders)
env.AddMethod(BuildProjectLibraries)
env.AddMethod(ConfigureProjectLibBuilder)
return env

View File

@ -30,9 +30,10 @@ from platformio.managers.core import get_core_package_dir
class InoToCPPConverter(object):
PROTOTYPE_RE = re.compile(r"""^(
PROTOTYPE_RE = re.compile(
r"""^(
(?:template\<.*\>\s*)? # template
([a-z_\d]+\*?\s+){1,2} # return type
([a-z_\d\&]+\*?\s+){1,2} # return type
([a-z_\d]+\s*) # name of prototype
\([a-z_,\.\*\&\[\]\s\d]*\) # arguments
)\s*\{ # must end with {
@ -89,8 +90,8 @@ class InoToCPPConverter(object):
self.env.Execute(
self.env.VerboseAction(
'$CXX -o "{0}" -x c++ -fpreprocessed -dD -E "{1}"'.format(
out_file,
tmp_path), "Converting " + basename(out_file[:-4])))
out_file, tmp_path),
"Converting " + basename(out_file[:-4])))
atexit.register(_delete_file, tmp_path)
return isfile(out_file)
@ -163,18 +164,17 @@ class InoToCPPConverter(object):
prototype_names = set([m.group(3).strip() for m in prototypes])
split_pos = prototypes[0].start()
match_ptrs = re.search(self.PROTOPTRS_TPLRE %
("|".join(prototype_names)),
contents[:split_pos], re.M)
match_ptrs = re.search(
self.PROTOPTRS_TPLRE % ("|".join(prototype_names)),
contents[:split_pos], re.M)
if match_ptrs:
split_pos = contents.rfind("\n", 0, match_ptrs.start()) + 1
result = []
result.append(contents[:split_pos].strip())
result.append("%s;" % ";\n".join([m.group(1) for m in prototypes]))
result.append('#line %d "%s"' %
(self._get_total_lines(contents[:split_pos]),
self._main_ino.replace("\\", "/")))
result.append('#line %d "%s"' % (self._get_total_lines(
contents[:split_pos]), self._main_ino.replace("\\", "/")))
result.append(contents[split_pos:].strip())
return "\n".join(result)
@ -231,14 +231,25 @@ def GetActualLDScript(env):
return None
script = None
script_in_next = False
for f in env.get("LINKFLAGS", []):
if f.startswith("-Wl,-T"):
script = env.subst(f[6:].replace('"', "").strip())
if isfile(script):
return script
path = _lookup_in_ldpath(script)
if path:
return path
raw_script = None
if f == "-T":
script_in_next = True
continue
elif script_in_next:
script_in_next = False
raw_script = f
elif f.startswith("-Wl,-T"):
raw_script = f[6:]
else:
continue
script = env.subst(raw_script.replace('"', "").strip())
if isfile(script):
return script
path = _lookup_in_ldpath(script)
if path:
return path
if script:
sys.stderr.write(
@ -295,9 +306,6 @@ def ProcessTest(env):
src_filter.append("+<%s%s>" % (env['PIOTEST'], sep))
env.Replace(PIOTEST_SRC_FILTER=src_filter)
return env.CollectBuildFiles("$BUILDTEST_DIR", "$PROJECTTEST_DIR",
"$PIOTEST_SRC_FILTER")
def GetExtraScripts(env, scope):
items = []

View File

@ -209,9 +209,9 @@ def CheckUploadSize(_, target, source, env): # pylint: disable=W0613,W0621
used_size = int(values[0]) + int(values[1])
if used_size > max_size:
sys.stderr.write("Error: The program size (%d bytes) is greater "
"than maximum allowed (%s bytes)\n" % (used_size,
max_size))
sys.stderr.write(
"Error: The program size (%d bytes) is greater "
"than maximum allowed (%s bytes)\n" % (used_size, max_size))
env.Exit(1)

View File

@ -22,7 +22,7 @@ from os.path import basename, dirname, isdir, join, realpath
from SCons import Builder, Util
from SCons.Script import (COMMAND_LINE_TARGETS, AlwaysBuild,
DefaultEnvironment, SConscript)
DefaultEnvironment, Export, SConscript)
from platformio.util import glob_escape, pioversion_to_intstr
@ -41,6 +41,43 @@ def scons_patched_match_splitext(path, suffixes=None):
return tokens
def _build_project_deps(env):
project_lib_builder = env.ConfigureProjectLibBuilder()
# append project libs to the beginning of list
env.Prepend(LIBS=project_lib_builder.build())
# append extra linker related options from libs
env.AppendUnique(
**{
key: project_lib_builder.env.get(key)
for key in ("LIBS", "LIBPATH", "LINKFLAGS")
if project_lib_builder.env.get(key)
})
if "__test" in COMMAND_LINE_TARGETS:
env.ProcessTest()
projenv = env.Clone()
projenv.BuildSources("$BUILDTEST_DIR", "$PROJECTTEST_DIR",
"$PIOTEST_SRC_FILTER")
else:
projenv = env.Clone()
projenv.BuildSources("$BUILDSRC_DIR", "$PROJECTSRC_DIR",
env.get("SRC_FILTER"))
# CPPPATH from dependencies
projenv.PrependUnique(CPPPATH=project_lib_builder.env.get("CPPPATH"))
# extra build flags from `platformio.ini`
projenv.ProcessFlags(env.get("SRC_BUILD_FLAGS"))
if not env.get("PIOBUILDFILES") and not COMMAND_LINE_TARGETS:
sys.stderr.write(
"Error: Nothing to build. Please put your source code files "
"to '%s' folder\n" % env.subst("$PROJECTSRC_DIR"))
env.Exit(1)
Export("projenv")
def BuildProgram(env):
def _append_pio_macros():
@ -62,6 +99,7 @@ def BuildProgram(env):
# process extra flags from board
if "BOARD" in env and "build.extra_flags" in env.BoardConfig():
env.ProcessFlags(env.BoardConfig().get("build.extra_flags"))
# apply user flags
env.ProcessFlags(env.get("BUILD_FLAGS"))
@ -74,37 +112,19 @@ def BuildProgram(env):
# remove specified flags
env.ProcessUnFlags(env.get("BUILD_UNFLAGS"))
# build dependent libs; place them before built-in libs
env.Prepend(LIBS=env.BuildProjectLibraries())
# build project with dependencies
_build_project_deps(env)
# append specified LD_SCRIPT
if ("LDSCRIPT_PATH" in env
# append into the beginning a main LD script
if (env.get("LDSCRIPT_PATH")
and not any("-Wl,-T" in f for f in env['LINKFLAGS'])):
env.Append(LINKFLAGS=['-Wl,-T"$LDSCRIPT_PATH"'])
env.Prepend(LINKFLAGS=["-T", "$LDSCRIPT_PATH"])
# enable "cyclic reference" for linker
if env.get("LIBS") and env.GetCompilerType() == "gcc":
env.Prepend(_LIBFLAGS="-Wl,--start-group ")
env.Append(_LIBFLAGS=" -Wl,--end-group")
# Handle SRC_BUILD_FLAGS
env.ProcessFlags(env.get("SRC_BUILD_FLAGS"))
if "__test" in COMMAND_LINE_TARGETS:
env.Append(PIOBUILDFILES=env.ProcessTest())
else:
env.Append(
PIOBUILDFILES=env.CollectBuildFiles(
"$BUILDSRC_DIR",
"$PROJECTSRC_DIR",
src_filter=env.get("SRC_FILTER")))
if not env['PIOBUILDFILES'] and not COMMAND_LINE_TARGETS:
sys.stderr.write(
"Error: Nothing to build. Please put your source code files "
"to '%s' folder\n" % env.subst("$PROJECTSRC_DIR"))
env.Exit(1)
program = env.Program(
join("$BUILD_DIR", env.subst("$PROGNAME")), env['PIOBUILDFILES'])
@ -171,7 +191,18 @@ def ProcessFlags(env, flags): # pylint: disable=too-many-branches
def ProcessUnFlags(env, flags):
if not flags:
return
for key, unflags in env.ParseFlagsExtended(flags).items():
parsed = env.ParseFlagsExtended(flags)
# get all flags and copy them to each "*FLAGS" variable
all_flags = []
for key, unflags in parsed.items():
if key.endswith("FLAGS"):
all_flags.extend(unflags)
for key, unflags in parsed.items():
if key.endswith("FLAGS"):
parsed[key].extend(all_flags)
for key, unflags in parsed.items():
for unflag in unflags:
for current in env.get(key, []):
conditions = [
@ -275,7 +306,7 @@ def BuildFrameworks(env, frameworks):
env.ConvertInoToCpp()
if f in board_frameworks:
SConscript(env.GetFrameworkScript(f))
SConscript(env.GetFrameworkScript(f), exports="env")
else:
sys.stderr.write(
"Error: This board doesn't support %s framework!\n" % f)
@ -289,8 +320,9 @@ def BuildLibrary(env, variant_dir, src_dir, src_filter=None):
def BuildSources(env, variant_dir, src_dir, src_filter=None):
nodes = env.CollectBuildFiles(variant_dir, src_dir, src_filter)
DefaultEnvironment().Append(
PIOBUILDFILES=env.CollectBuildFiles(variant_dir, src_dir, src_filter))
PIOBUILDFILES=[env.Object(node) for node in nodes])
def exists(_):

View File

@ -439,8 +439,8 @@ def lib_stats(json_output):
printitem_tpl.format(
name=click.style(name, fg="cyan"),
url=click.style(
"https://platformio.org/lib/search?query=" +
quote("keyword:%s" % name),
"https://platformio.org/lib/search?query=" + quote(
"keyword:%s" % name),
fg="blue")))
for key in ("updated", "added"):

View File

@ -273,8 +273,8 @@ def platform_show(platform, json_output): # pylint: disable=too-many-branches
if item['type']:
click.echo("Type: %s" % item['type'])
click.echo("Requirements: %s" % item['requirements'])
click.echo("Installed: %s" %
("Yes" if item.get("version") else "No (optional)"))
click.echo("Installed: %s" % ("Yes" if item.get("version") else
"No (optional)"))
if "version" in item:
click.echo("Version: %s" % item['version'])
if "originalVersion" in item:

View File

@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import datetime
from hashlib import sha1
from os import getcwd, makedirs, walk
from os.path import getmtime, isdir, isfile, join
@ -197,10 +196,8 @@ class EnvironmentProcessor(object):
"%s: %s" % (k, ", ".join(util.parse_conf_multi_values(v))))
if not self.silent:
click.echo("[%s] Processing %s (%s)" %
(datetime.now().strftime("%c"),
click.style(self.name, fg="cyan", bold=True),
"; ".join(env_dump)))
click.echo("Processing %s (%s)" % (click.style(
self.name, fg="cyan", bold=True), "; ".join(env_dump)))
click.secho("-" * terminal_width, bold=True)
self.options = self._validate_options(self.options)
@ -296,10 +293,10 @@ class EnvironmentProcessor(object):
if d.strip()
], self.verbose)
if "lib_deps" in self.options:
_autoinstall_libdeps(self.cmd_ctx,
util.parse_conf_multi_values(
self.options['lib_deps']),
self.verbose)
_autoinstall_libdeps(
self.cmd_ctx,
util.parse_conf_multi_values(self.options['lib_deps']),
self.verbose)
try:
p = PlatformFactory.newPlatform(self.options['platform'])
@ -385,10 +382,10 @@ def print_summary(results, start_time):
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),
"[%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

@ -32,8 +32,8 @@ def settings_get(name):
click.echo(
list_tpl.format(
name=click.style("Name", fg="cyan"),
value=(click.style("Value", fg="green") +
click.style(" [Default]", fg="yellow")),
value=(click.style("Value", fg="green") + click.style(
" [Default]", fg="yellow")),
description="Description"))
click.echo("-" * terminal_width)

View File

@ -43,9 +43,8 @@ class FileDownloader(object):
disposition = self._request.headers.get("content-disposition")
if disposition and "filename=" in disposition:
self._fname = disposition[
disposition.index("filename=") + 9:].replace('"', "").replace(
"'", "")
self._fname = disposition[disposition.index("filename=") +
9:].replace('"', "").replace("'", "")
self._fname = self._fname.encode("utf8")
else:
self._fname = [p for p in url.split("/") if p][-1]

View File

@ -192,6 +192,11 @@ class InvalidLibConfURL(PlatformioException):
MESSAGE = "Invalid library config URL '{0}'"
class InvalidProjectConf(PlatformioException):
MESSAGE = "Invalid `platformio.ini`, project configuration file: '{0}'"
class BuildScriptNotFound(PlatformioException):
MESSAGE = "Invalid path '{0}' to build script"
@ -248,7 +253,7 @@ class CygwinEnvDetected(PlatformioException):
class DebugSupportError(PlatformioException):
MESSAGE = ("Currently, PlatformIO does not support debugging for `{0}`.\n"
"Please mail contact@pioplus.com or visit "
"Please contact support@pioplus.com or visit "
"< http://docs.platformio.org/page/plus/debugging.html >")

View File

@ -18,6 +18,7 @@
"name": "Win32",
% elif systype == "darwin":
"name": "Mac",
"macFrameworkPath": [],
% else:
"name": "Linux",
% end

View File

@ -208,8 +208,8 @@ def after_upgrade(ctx):
# PlatformIO banner
click.echo("*" * terminal_width)
click.echo("If you like %s, please:" %
(click.style("PlatformIO", fg="cyan")))
click.echo(
"If you like %s, please:" % (click.style("PlatformIO", fg="cyan")))
click.echo("- %s us on Twitter to stay up-to-date "
"on the latest project news > %s" %
(click.style("follow", fg="cyan"),
@ -224,9 +224,9 @@ def after_upgrade(ctx):
(click.style("try", fg="cyan"),
click.style("https://platformio.org/platformio-ide", fg="cyan")))
if not util.is_ci():
click.echo("- %s us with PlatformIO Plus > %s" %
(click.style("support", fg="cyan"),
click.style("https://pioplus.com", fg="cyan")))
click.echo("- %s us with PlatformIO Plus > %s" % (click.style(
"support", fg="cyan"), click.style(
"https://pioplus.com", fg="cyan")))
click.echo("*" * terminal_width)
click.echo("")
@ -296,8 +296,8 @@ def check_internal_updates(ctx, what):
if manifest['name'] in outdated_items:
continue
conds = [
pm.outdated(manifest['__pkg_dir']),
what == "platforms" and PlatformFactory.newPlatform(
pm.outdated(manifest['__pkg_dir']), what == "platforms"
and PlatformFactory.newPlatform(
manifest['__pkg_dir']).are_outdated_packages()
]
if any(conds):
@ -318,8 +318,8 @@ def check_internal_updates(ctx, what):
if not app.get_setting("auto_update_" + what):
click.secho("Please update them via ", fg="yellow", nl=False)
click.secho(
"`platformio %s update`" %
("lib --global" if what == "libraries" else "platform"),
"`platformio %s update`" % ("lib --global" if what == "libraries"
else "platform"),
fg="cyan",
nl=False)
click.secho(" command.\n", fg="yellow")

View File

@ -107,6 +107,7 @@ def pioplus_call(args, **kwargs):
pythonexe_path = util.get_pythonexe_path()
os.environ['PYTHONEXEPATH'] = pythonexe_path
os.environ['PYTHONPYSITEDIR'] = get_core_package_dir("contrib-pysite")
os.environ['PIOCOREPYSITEDIR'] = dirname(util.get_source_dir() or "")
os.environ['PATH'] = (os.pathsep).join(
[dirname(pythonexe_path), os.environ['PATH']])
util.copy_pythonpath_to_osenv()

View File

@ -208,9 +208,9 @@ class LibraryManager(BasePkgManager):
cache_valid="30d")
assert dl_data
return self._install_from_url(name, dl_data['url'].replace(
"http://", "https://") if app.get_setting("enable_ssl") else
dl_data['url'], requirements)
return self._install_from_url(
name, dl_data['url'].replace("http://", "https://")
if app.get_setting("enable_ssl") else dl_data['url'], requirements)
def search_lib_id( # pylint: disable=too-many-branches
self,
@ -237,9 +237,9 @@ class LibraryManager(BasePkgManager):
if not isinstance(values, list):
values = [v.strip() for v in values.split(",") if v]
for value in values:
query.append('%s:"%s"' % (key[:-1]
if key.endswith("s") else key,
value))
query.append(
'%s:"%s"' % (key[:-1]
if key.endswith("s") else key, value))
lib_info = None
result = util.get_api_result(
@ -290,9 +290,9 @@ class LibraryManager(BasePkgManager):
def _get_lib_id_from_installed(self, filters):
if filters['name'].startswith("id="):
return int(filters['name'][3:])
package_dir = self.get_package_dir(filters['name'],
filters.get("requirements",
filters.get("version")))
package_dir = self.get_package_dir(
filters['name'], filters.get("requirements",
filters.get("version")))
if not package_dir:
return None
manifest = self.load_manifest(package_dir)

View File

@ -18,7 +18,7 @@ import json
import os
import re
import shutil
from os.path import basename, getsize, isdir, isfile, islink, join
from os.path import abspath, basename, getsize, isdir, isfile, islink, join
from tempfile import mkdtemp
import click
@ -90,7 +90,8 @@ class PkgRepoMixin(object):
reqspec = None
if requirements:
try:
reqspec = semantic_version.Spec(requirements)
reqspec = self.parse_semver_spec(
requirements, raise_exception=True)
except ValueError:
pass
@ -98,8 +99,8 @@ class PkgRepoMixin(object):
if not self.is_system_compatible(v.get("system")):
continue
if "platformio" in v.get("engines", {}):
if PkgRepoMixin.PIO_VERSION not in semantic_version.Spec(
v['engines']['platformio']):
if PkgRepoMixin.PIO_VERSION not in self.parse_semver_spec(
v['engines']['platformio'], raise_exception=True):
continue
specver = semantic_version.Version(v['version'])
if reqspec and specver not in reqspec:
@ -224,7 +225,20 @@ class PkgInstallerMixin(object):
@staticmethod
def parse_semver_spec(value, raise_exception=False):
try:
return semantic_version.Spec(value)
# Workaround for ^ issue and pre-releases
# https://github.com/rbarrois/python-semanticversion/issues/61
requirements = []
for item in str(value).split(","):
item = item.strip()
if not item:
continue
if item.startswith("^"):
major = semantic_version.Version.coerce(item[1:]).major
requirements.append(">=%s" % major)
requirements.append("<%s" % (int(major) + 1))
else:
requirements.append(item)
return semantic_version.Spec(*requirements)
except ValueError as e:
if raise_exception:
raise e
@ -367,6 +381,12 @@ class PkgInstallerMixin(object):
return manifest.get("__pkg_dir") if manifest and isdir(
manifest.get("__pkg_dir")) else None
def get_package_by_dir(self, pkg_dir):
for manifest in self.get_installed():
if manifest['__pkg_dir'] == util.path_to_unicode(abspath(pkg_dir)):
return manifest
return None
def find_pkg_root(self, src_dir):
if self.manifest_exists(src_dir):
return src_dir
@ -474,8 +494,8 @@ class PkgInstallerMixin(object):
"Package version %s doesn't satisfy requirements %s" %
(tmp_manifest['version'], requirements))
try:
assert tmp_semver and tmp_semver in semantic_version.Spec(
requirements), mismatch_error
assert tmp_semver and tmp_semver in self.parse_semver_spec(
requirements, raise_exception=True), mismatch_error
except (AssertionError, ValueError):
assert tmp_manifest['version'] == requirements, mismatch_error
@ -500,8 +520,8 @@ class PkgInstallerMixin(object):
cur_manifest['version'])
if "__src_url" in cur_manifest:
target_dirname = "%s@src-%s" % (
pkg_dirname,
hashlib.md5(cur_manifest['__src_url']).hexdigest())
pkg_dirname, hashlib.md5(
cur_manifest['__src_url']).hexdigest())
shutil.move(pkg_dir, join(self.package_dir, target_dirname))
# fix to a version
elif action == 2:
@ -509,8 +529,8 @@ class PkgInstallerMixin(object):
tmp_manifest['version'])
if "__src_url" in tmp_manifest:
target_dirname = "%s@src-%s" % (
pkg_dirname,
hashlib.md5(tmp_manifest['__src_url']).hexdigest())
pkg_dirname, hashlib.md5(
tmp_manifest['__src_url']).hexdigest())
pkg_dir = join(self.package_dir, target_dirname)
# remove previous/not-satisfied package
@ -715,20 +735,20 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
return pkg_dir
def uninstall(self, package, requirements=None, after_update=False):
if isdir(package):
if isdir(package) and self.get_package_by_dir(package):
pkg_dir = package
else:
name, requirements, url = self.parse_pkg_uri(package, requirements)
pkg_dir = self.get_package_dir(name, requirements, url)
if not pkg_dir:
raise exception.UnknownPackage("%s @ %s" % (package,
requirements or "*"))
raise exception.UnknownPackage(
"%s @ %s" % (package, requirements or "*"))
manifest = self.load_manifest(pkg_dir)
click.echo(
"Uninstalling %s @ %s: \t" %
(click.style(manifest['name'], fg="cyan"), manifest['version']),
"Uninstalling %s @ %s: \t" % (click.style(
manifest['name'], fg="cyan"), manifest['version']),
nl=False)
if islink(pkg_dir):
@ -740,9 +760,9 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
# unfix package with the same name
pkg_dir = self.get_package_dir(manifest['name'])
if pkg_dir and "@" in pkg_dir:
shutil.move(pkg_dir,
join(self.package_dir,
self.get_install_dirname(manifest)))
shutil.move(
pkg_dir,
join(self.package_dir, self.get_install_dirname(manifest)))
self.cache_reset()
click.echo("[%s]" % click.style("OK", fg="green"))
@ -755,14 +775,14 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
return True
def update(self, package, requirements=None, only_check=False):
if isdir(package):
if isdir(package) and self.get_package_by_dir(package):
pkg_dir = package
else:
pkg_dir = self.get_package_dir(*self.parse_pkg_uri(package))
if not pkg_dir:
raise exception.UnknownPackage("%s @ %s" % (package,
requirements or "*"))
raise exception.UnknownPackage(
"%s @ %s" % (package, requirements or "*"))
manifest = self.load_manifest(pkg_dir)
name = manifest['name']

View File

@ -39,9 +39,9 @@ class PlatformManager(BasePkgManager):
"{0}://dl.platformio.org/platforms/manifest.json".format(
"https" if app.get_setting("enable_ssl") else "http")
]
BasePkgManager.__init__(self, package_dir
or join(util.get_home_dir(), "platforms"),
repositories)
BasePkgManager.__init__(
self, package_dir or join(util.get_home_dir(), "platforms"),
repositories)
@property
def manifest_names(self):
@ -331,8 +331,8 @@ class PlatformPackagesMixin(object):
def get_package_dir(self, name):
version = self.packages[name].get("version", "")
if ":" in version:
return self.pm.get_package_dir(*self.pm.parse_pkg_uri(
"%s=%s" % (name, version)))
return self.pm.get_package_dir(
*self.pm.parse_pkg_uri("%s=%s" % (name, version)))
return self.pm.get_package_dir(name, version)
def get_package_version(self, name):

View File

@ -36,30 +36,40 @@ from platformio import __apiurl__, __version__, exception
# pylint: disable=wrong-import-order, too-many-ancestors
try:
from configparser import ConfigParser
import configparser as ConfigParser
except ImportError:
from ConfigParser import ConfigParser
import ConfigParser as ConfigParser
class ProjectConfig(ConfigParser):
class ProjectConfig(ConfigParser.ConfigParser):
VARTPL_RE = re.compile(r"\$\{([^\.\}]+)\.([^\}]+)\}")
def items(self, section, **_): # pylint: disable=arguments-differ
items = []
for option in ConfigParser.options(self, section):
for option in ConfigParser.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)
try:
value = ConfigParser.ConfigParser.get(self, section, option,
**kwargs)
except ConfigParser.Error as e:
raise exception.InvalidProjectConf(str(e))
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):
section, option = match.group(1), match.group(2)
if section == "env" and not self.has_section(section):
if section in ("env", "sysenv") and not self.has_section(section):
if section == "env":
click.secho(
"Warning! Access to system environment variable via "
"`${{env.{0}}}` is deprecated. Please use "
"`${{sysenv.{0}}}` instead".format(option),
fg="yellow")
return os.getenv(option)
return self.get(section, option)
@ -331,7 +341,10 @@ def load_project_config(path=None):
raise exception.NotPlatformIOProject(
dirname(path) if path.endswith("platformio.ini") else path)
cp = ProjectConfig()
cp.read(path)
try:
cp.read(path)
except ConfigParser.Error as e:
raise exception.InvalidProjectConf(str(e))
return cp

View File

@ -29,8 +29,9 @@ build_flags = %s
""" % " ".join([f[0] for f in build_flags]))
tmpdir.join("extra.py").write("""
Import("env")
env.Append(CPPDEFINES="POST_SCRIPT_MACRO")
Import("projenv")
projenv.Append(CPPDEFINES="POST_SCRIPT_MACRO")
""")
tmpdir.mkdir("src").join("main.cpp").write("""