Merge branch 'release/v4.0.3'

This commit is contained in:
Ivan Kravets
2019-08-30 15:41:59 +03:00
26 changed files with 198 additions and 134 deletions

View File

@ -6,6 +6,17 @@ Release Notes
PlatformIO 4.0 PlatformIO 4.0
-------------- --------------
4.0.3 (2019-08-30)
~~~~~~~~~~~~~~~~~~
* Added support for multi-environment PlatformIO project for `CLion IDE <http://docs.platformio.org/page/ide/clion.html>`__ (`issue #2824 <https://github.com/platformio/platformio-core/issues/2824>`_)
* Generate ``.ccls`` LSP file for `Vim <http://docs.platformio.org/en/page/vim.html>`__ cross references, hierarchies, completion and semantic highlighting (`issue #2952 <https://github.com/platformio/platformio-core/issues/2952>`_)
* Added support for `PLATFORMIO_DISABLE_COLOR <http://docs.platformio.org/page/envvars.html#envvar-PLATFORMIO_DISABLE_COLOR>`__ system environment variable which disables color ANSI-codes in a terminal output (`issue #2956 <https://github.com/platformio/platformio-core/issues/2956>`_)
* Updated SCons tool to 3.1.1
* Remove ProjectConfig cache when "platformio.ini" was modified outside
* Fixed an issue with PIO Unified Debugger on Windows OS when debug server is piped
* Fixed an issue when `--upload-port <http://docs.platformio.org/page/userguide/cmd_run.html#cmdoption-platformio-run-upload-port>`__ CLI flag does not override declared `upload_port <http://docs.platformio.org/page/projectconf/section_env_upload.html#upload-port>`__ option in `"platformio.ini" (Project Configuration File) <https://docs.platformio.org/page/projectconf.html>`__
4.0.2 (2019-08-23) 4.0.2 (2019-08-23)
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~

2
docs

Submodule docs updated: 3f5d12ca25...704ff85c7d

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
VERSION = (4, 0, 2) VERSION = (4, 0, 3)
__version__ = ".".join([str(s) for s in VERSION]) __version__ = ".".join([str(s) for s in VERSION])
__title__ = "platformio" __title__ = "platformio"

View File

@ -55,13 +55,15 @@ def configure():
except (AttributeError, ImportError): except (AttributeError, ImportError):
pass pass
# handle PLATFORMIO_FORCE_COLOR try:
if str(os.getenv("PLATFORMIO_FORCE_COLOR", "")).lower() == "true": if str(os.getenv("PLATFORMIO_DISABLE_COLOR", "")).lower() == "true":
try: # pylint: disable=protected-access
click._compat.isatty = lambda stream: False
elif str(os.getenv("PLATFORMIO_FORCE_COLOR", "")).lower() == "true":
# pylint: disable=protected-access # pylint: disable=protected-access
click._compat.isatty = lambda stream: True click._compat.isatty = lambda stream: True
except: # pylint: disable=bare-except except: # pylint: disable=bare-except
pass pass
# Handle IOError issue with VSCode's Terminal (Windows) # Handle IOError issue with VSCode's Terminal (Windows)
click_echo_origin = [click.echo, click.secho] click_echo_origin = [click.echo, click.secho]

View File

@ -140,6 +140,8 @@ def DumpIDEData(env, projenv):
LINTCXXCOM = "$CXXFLAGS $CCFLAGS $CPPFLAGS" LINTCXXCOM = "$CXXFLAGS $CCFLAGS $CPPFLAGS"
data = { data = {
"env_name":
env['PIOENV'],
"libsource_dirs": [env.subst(l) for l in env.GetLibSourceDirs()], "libsource_dirs": [env.subst(l) for l in env.GetLibSourceDirs()],
"defines": "defines":
_dump_defines(env), _dump_defines(env),

View File

@ -300,20 +300,8 @@ class LibBuilderBase(object):
]) ])
return items return items
def _validate_search_files(self, search_files=None): def _get_found_includes( # pylint: disable=too-many-branches
if not search_files: self, search_files=None):
search_files = []
assert isinstance(search_files, list)
_search_files = []
for path in search_files:
if path not in self._processed_files:
_search_files.append(path)
self._processed_files.append(path)
return _search_files
def _get_found_includes(self, search_files=None):
# all include directories # all include directories
if not LibBuilderBase._INCLUDE_DIRS_CACHE: if not LibBuilderBase._INCLUDE_DIRS_CACHE:
LibBuilderBase._INCLUDE_DIRS_CACHE = [] LibBuilderBase._INCLUDE_DIRS_CACHE = []
@ -326,7 +314,11 @@ class LibBuilderBase(object):
include_dirs.extend(LibBuilderBase._INCLUDE_DIRS_CACHE) include_dirs.extend(LibBuilderBase._INCLUDE_DIRS_CACHE)
result = [] result = []
for path in self._validate_search_files(search_files): for path in (search_files or []):
if path in self._processed_files:
continue
self._processed_files.append(path)
try: try:
assert "+" in self.lib_ldf_mode assert "+" in self.lib_ldf_mode
candidates = LibBuilderBase.CCONDITIONAL_SCANNER( candidates = LibBuilderBase.CCONDITIONAL_SCANNER(
@ -334,6 +326,11 @@ class LibBuilderBase(object):
self.env, self.env,
tuple(include_dirs), tuple(include_dirs),
depth=self.CCONDITIONAL_SCANNER_DEPTH) depth=self.CCONDITIONAL_SCANNER_DEPTH)
# mark candidates already processed via Conditional Scanner
self._processed_files.extend([
c.get_abspath() for c in candidates
if c.get_abspath() not in self._processed_files
])
except Exception as e: # pylint: disable=broad-except except Exception as e: # pylint: disable=broad-except
if self.verbose and "+" in self.lib_ldf_mode: if self.verbose and "+" in self.lib_ldf_mode:
sys.stderr.write( sys.stderr.write(

View File

@ -32,7 +32,8 @@ def GetProjectOption(env, option, default=None):
def LoadProjectOptions(env): def LoadProjectOptions(env):
for option, value in env.GetProjectOptions(): for option, value in env.GetProjectOptions():
option_meta = ProjectOptions.get("env." + option) option_meta = ProjectOptions.get("env." + option)
if not option_meta or not option_meta.buildenvvar: if (not option_meta or not option_meta.buildenvvar
or option_meta.buildenvvar in env):
continue continue
env[option_meta.buildenvvar] = value env[option_meta.buildenvvar] = value

View File

@ -19,7 +19,7 @@ from hashlib import sha1
from io import BytesIO from io import BytesIO
from os.path import isfile from os.path import isfile
from platformio import exception, util from platformio import exception, fs, util
from platformio.commands.platform import \ from platformio.commands.platform import \
platform_install as cmd_platform_install platform_install as cmd_platform_install
from platformio.commands.run import cli as cmd_run from platformio.commands.run import cli as cmd_run
@ -165,11 +165,11 @@ def configure_esp32_load_cmds(debug_options, configuration):
mon_cmds = [ mon_cmds = [
'monitor program_esp32 "{{{path}}}" {offset} verify'.format( 'monitor program_esp32 "{{{path}}}" {offset} verify'.format(
path=item['path'], offset=item['offset']) path=fs.to_unix_path(item['path']), offset=item['offset'])
for item in configuration.get("flash_extra_images") for item in configuration.get("flash_extra_images")
] ]
mon_cmds.append('monitor program_esp32 "{%s.bin}" 0x10000 verify' % mon_cmds.append('monitor program_esp32 "{%s.bin}" 0x10000 verify' %
configuration['prog_path'][:-4]) fs.to_unix_path(configuration['prog_path'][:-4]))
return mon_cmds return mon_cmds

View File

@ -17,6 +17,7 @@ import signal
import click import click
from twisted.internet import protocol # pylint: disable=import-error from twisted.internet import protocol # pylint: disable=import-error
from platformio import fs
from platformio.compat import string_types from platformio.compat import string_types
from platformio.proc import get_pythonexe_path from platformio.proc import get_pythonexe_path
from platformio.project.helpers import get_project_core_dir from platformio.project.helpers import get_project_core_dir
@ -38,6 +39,10 @@ class BaseProcess(protocol.ProcessProtocol, object):
_patterns = self.COMMON_PATTERNS.copy() _patterns = self.COMMON_PATTERNS.copy()
_patterns.update(patterns or {}) _patterns.update(patterns or {})
for key, value in _patterns.items():
if key.endswith(("_DIR", "_PATH")):
_patterns[key] = fs.to_unix_path(value)
def _replace(text): def _replace(text):
for key, value in _patterns.items(): for key, value in _patterns.items():
pattern = "$%s" % key pattern = "$%s" % key

View File

@ -18,7 +18,7 @@ from os.path import isdir, isfile, join
from twisted.internet import error # pylint: disable=import-error from twisted.internet import error # pylint: disable=import-error
from twisted.internet import reactor # pylint: disable=import-error from twisted.internet import reactor # pylint: disable=import-error
from platformio import exception, util from platformio import exception, fs, util
from platformio.commands.debug.process import BaseProcess from platformio.commands.debug.process import BaseProcess
from platformio.proc import where_is_program from platformio.proc import where_is_program
@ -74,7 +74,7 @@ class DebugServer(BaseProcess):
str_args = " ".join( str_args = " ".join(
[arg if arg.startswith("-") else '"%s"' % arg for arg in args]) [arg if arg.startswith("-") else '"%s"' % arg for arg in args])
self._debug_port = '| "%s" %s' % (server_executable, str_args) self._debug_port = '| "%s" %s' % (server_executable, str_args)
self._debug_port = self._debug_port.replace("\\", "\\\\") self._debug_port = fs.to_unix_path(self._debug_port)
else: else:
env = os.environ.copy() env = os.environ.copy()
# prepend server "lib" folder to LD path # prepend server "lib" folder to LD path

View File

@ -388,7 +388,6 @@ def fill_project_envs(ctx, project_dir, board_ids, project_option, env_prefix,
if modified: if modified:
config.save() config.save()
config.reset_instances()
def _install_dependent_platforms(ctx, platforms): def _install_dependent_platforms(ctx, platforms):

View File

@ -23,7 +23,7 @@ from glob import glob
import click import click
from platformio import exception from platformio import exception
from platformio.compat import get_file_contents, glob_escape from platformio.compat import WINDOWS, get_file_contents, glob_escape
class cd(object): class cd(object):
@ -146,6 +146,12 @@ def match_src_files(src_dir, src_filter=None, src_exts=None):
return sorted(list(matches)) return sorted(list(matches))
def to_unix_path(path):
if not WINDOWS or not path:
return path
return re.sub(r"[\\]+", "/", path)
def rmtree(path): def rmtree(path):
def _onerror(func, path, __): def _onerror(func, path, __):

View File

@ -14,14 +14,13 @@
import codecs import codecs
import os import os
import re
import sys import sys
from os.path import abspath, basename, expanduser, isdir, isfile, join, relpath from os.path import abspath, basename, expanduser, isdir, isfile, join, relpath
import bottle import bottle
from platformio import fs, util from platformio import fs, util
from platformio.compat import WINDOWS, get_file_contents from platformio.compat import get_file_contents
from platformio.proc import where_is_program from platformio.proc import where_is_program
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
from platformio.project.helpers import (get_project_lib_dir, from platformio.project.helpers import (get_project_lib_dir,
@ -98,21 +97,15 @@ class ProjectGenerator(object):
for key, value in tpl_vars.items(): for key, value in tpl_vars.items():
if key.endswith(("_path", "_dir")): if key.endswith(("_path", "_dir")):
tpl_vars[key] = self.to_unix_path(value) tpl_vars[key] = fs.to_unix_path(value)
for key in ("includes", "src_files", "libsource_dirs"): for key in ("includes", "src_files", "libsource_dirs"):
if key not in tpl_vars: if key not in tpl_vars:
continue continue
tpl_vars[key] = [self.to_unix_path(inc) for inc in tpl_vars[key]] tpl_vars[key] = [fs.to_unix_path(inc) for inc in tpl_vars[key]]
tpl_vars['to_unix_path'] = self.to_unix_path tpl_vars['to_unix_path'] = fs.to_unix_path
return tpl_vars return tpl_vars
@staticmethod
def to_unix_path(path):
if not WINDOWS or not path:
return path
return re.sub(r"[\\]+", "/", path)
def get_src_files(self): def get_src_files(self):
result = [] result = []
with fs.cd(self.project_dir): with fs.cd(self.project_dir):

View File

@ -15,10 +15,18 @@
<config projectName="{{project_name}}" targetName="DEBUG" /> <config projectName="{{project_name}}" targetName="DEBUG" />
</generated> </generated>
</component> </component>
<component name="CMakeSettings" AUTO_RELOAD="true" GENERATION_PASS_SYSTEM_ENVIRONMENT="true"> <component name="CMakeSettings" AUTO_RELOAD="true">
<ADDITIONAL_GENERATION_ENVIRONMENT> <configurations>
<envs /> % envs = config.envs()
</ADDITIONAL_GENERATION_ENVIRONMENT> % if len(envs) > 1:
% for env in envs:
<configuration PROFILE_NAME="{{ env }}" CONFIG_NAME="{{ env }}" />
% end
<configuration PROFILE_NAME="All" CONFIG_NAME="All" />
% else:
<configuration PROFILE_NAME="{{ env_name }}" CONFIG_NAME="{{ env_name }}" />
% end
</configurations>
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="ec922180-b3d3-40f1-af0b-2568113a9075" name="Default" comment="" /> <list default="true" id="ec922180-b3d3-40f1-af0b-2568113a9075" name="Default" comment="" />
@ -163,10 +171,6 @@
<envs /> <envs />
<method /> <method />
</configuration> </configuration>
<configuration default="false" name="Build All" type="CMakeRunConfiguration" factoryName="Application" WORKING_DIR="" PASS_PARENT_ENVS="FALSE" CONFIG_NAME="Debug" EXPLICIT_BUILD_TARGET_NAME="all">
<envs />
<method />
</configuration>
<configuration default="false" name="PLATFORMIO_BUILD" type="CMakeRunConfiguration" factoryName="Application" WORKING_DIR="" PASS_PARENT_ENVS="FALSE" PROJECT_NAME="{{project_name}}" TARGET_NAME="PLATFORMIO_BUILD" CONFIG_NAME="Debug"> <configuration default="false" name="PLATFORMIO_BUILD" type="CMakeRunConfiguration" factoryName="Application" WORKING_DIR="" PASS_PARENT_ENVS="FALSE" PROJECT_NAME="{{project_name}}" TARGET_NAME="PLATFORMIO_BUILD" CONFIG_NAME="Debug">
<envs /> <envs />
<method /> <method />
@ -199,17 +203,6 @@
<envs /> <envs />
<method /> <method />
</configuration> </configuration>
<list size="9">
<item index="0" class="java.lang.String" itemvalue="Application.Build All" />
<item index="1" class="java.lang.String" itemvalue="Application.PLATFORMIO_BUILD" />
<item index="2" class="java.lang.String" itemvalue="Application.PLATFORMIO_UPLOAD" />
<item index="3" class="java.lang.String" itemvalue="Application.PLATFORMIO_CLEAN" />
<item index="4" class="java.lang.String" itemvalue="Application.PLATFORMIO_TEST" />
<item index="5" class="java.lang.String" itemvalue="Application.PLATFORMIO_PROGRAM" />
<item index="6" class="java.lang.String" itemvalue="Application.PLATFORMIO_UPLOADFS" />
<item index="7" class="java.lang.String" itemvalue="Application.PLATFORMIO_UPDATE" />
<item index="8" class="java.lang.String" itemvalue="Application.PLATFORMIO_REBUILD_PROJECT_INDEX" />
</list>
</component> </component>
<component name="ShelveChangesManager" show_recycled="false" /> <component name="ShelveChangesManager" show_recycled="false" />
<component name="SvnConfiguration"> <component name="SvnConfiguration">

View File

@ -16,49 +16,49 @@ endif()
add_custom_target( add_custom_target(
PLATFORMIO_BUILD ALL PLATFORMIO_BUILD ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run COMMAND ${PLATFORMIO_CMD} -f -c clion run "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
add_custom_target( add_custom_target(
PLATFORMIO_BUILD_VERBOSE ALL PLATFORMIO_BUILD_VERBOSE ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --verbose COMMAND ${PLATFORMIO_CMD} -f -c clion run --verbose "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
add_custom_target( add_custom_target(
PLATFORMIO_UPLOAD ALL PLATFORMIO_UPLOAD ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --target upload COMMAND ${PLATFORMIO_CMD} -f -c clion run --target upload "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
add_custom_target( add_custom_target(
PLATFORMIO_CLEAN ALL PLATFORMIO_CLEAN ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --target clean COMMAND ${PLATFORMIO_CMD} -f -c clion run --target clean "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
add_custom_target( add_custom_target(
PLATFORMIO_MONITOR ALL PLATFORMIO_MONITOR ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion device monitor COMMAND ${PLATFORMIO_CMD} -f -c clion device monitor "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
add_custom_target( add_custom_target(
PLATFORMIO_TEST ALL PLATFORMIO_TEST ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion test COMMAND ${PLATFORMIO_CMD} -f -c clion test "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
add_custom_target( add_custom_target(
PLATFORMIO_PROGRAM ALL PLATFORMIO_PROGRAM ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --target program COMMAND ${PLATFORMIO_CMD} -f -c clion run --target program "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
add_custom_target( add_custom_target(
PLATFORMIO_UPLOADFS ALL PLATFORMIO_UPLOADFS ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion run --target uploadfs COMMAND ${PLATFORMIO_CMD} -f -c clion run --target uploadfs "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
@ -74,4 +74,10 @@ add_custom_target(
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
add_custom_target(
PLATFORMIO_DEVICE_LIST ALL
COMMAND ${PLATFORMIO_CMD} -f -c clion device list
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_executable(${PROJECT_NAME} ${SRC_LIST}) add_executable(${PROJECT_NAME} ${SRC_LIST})

View File

@ -5,6 +5,8 @@
# please create `CMakeListsUser.txt` in the root of project. # please create `CMakeListsUser.txt` in the root of project.
# The `CMakeListsUser.txt` will not be overwritten by PlatformIO. # The `CMakeListsUser.txt` will not be overwritten by PlatformIO.
%from platformio.project.helpers import (load_project_ide_data)
%
% import re % import re
% %
% def _normalize_path(path): % def _normalize_path(path):
@ -19,6 +21,14 @@
% end % end
% return path % return path
% end % end
%
% envs = config.envs()
% if len(envs) > 1:
set(CMAKE_CONFIGURATION_TYPES "{{ ";".join(envs) }}" CACHE STRING "" FORCE)
% else:
set(CMAKE_CONFIGURATION_TYPES "{{ env_name }}" CACHE STRING "" FORCE)
% end
set(PLATFORMIO_CMD "{{ _normalize_path(platformio_path) }}") set(PLATFORMIO_CMD "{{ _normalize_path(platformio_path) }}")
@ -37,12 +47,31 @@ SET(CMAKE_C_STANDARD {{ cc_stds[-1] }})
set(CMAKE_CXX_STANDARD {{ cxx_stds[-1] }}) set(CMAKE_CXX_STANDARD {{ cxx_stds[-1] }})
% end % end
% for define in defines: if (CMAKE_BUILD_TYPE MATCHES "{{ env_name }}")
add_definitions(-D'{{!re.sub(r"([\"\(\)#])", r"\\\1", define)}}') %for define in defines:
% end add_definitions(-D'{{!re.sub(r"([\"\(\)#])", r"\\\1", define)}}')
%end
% for include in includes: %for include in includes:
include_directories("{{ _normalize_path(include) }}") include_directories("{{ _normalize_path(to_unix_path(include)) }}")
% end %end
endif()
% leftover_envs = set(envs) ^ set([env_name])
%
% if leftover_envs:
% ide_data = load_project_ide_data(project_dir, leftover_envs)
% end
%
% for env, data in ide_data.items():
if (CMAKE_BUILD_TYPE MATCHES "{{ env }}")
% for define in data["defines"]:
add_definitions(-D'{{!re.sub(r"([\"\(\)#])", r"\\\1", define)}}')
% end
% for include in data["includes"]:
include_directories("{{ _normalize_path(to_unix_path(include)) }}")
% end
endif()
% end
FILE(GLOB_RECURSE SRC_LIST "{{ _normalize_path(project_src_dir) }}/*.*" "{{ _normalize_path(project_lib_dir) }}/*.*" "{{ _normalize_path(project_libdeps_dir) }}/*.*") FILE(GLOB_RECURSE SRC_LIST "{{ _normalize_path(project_src_dir) }}/*.*" "{{ _normalize_path(project_lib_dir) }}/*.*" "{{ _normalize_path(project_libdeps_dir) }}/*.*")

View File

@ -0,0 +1,22 @@
% import re
% STD_RE = re.compile(r"\-std=[a-z\+]+(\d+)")
% cc_stds = STD_RE.findall(cc_flags)
% cxx_stds = STD_RE.findall(cxx_flags)
%
%
clang
% if cc_stds:
{{"%c"}} -std=c{{ cc_stds[-1] }}
% end
% if cxx_stds:
{{"%cpp"}} -std=c++{{ cxx_stds[-1] }}
% end
% for include in includes:
-I{{ include }}
% end
% for define in defines:
-D{{ define }}
% end

View File

@ -24,12 +24,12 @@ from platformio.proc import copy_pythonpath_to_osenv, get_pythonexe_path
from platformio.project.helpers import get_project_packages_dir from platformio.project.helpers import get_project_packages_dir
CORE_PACKAGES = { CORE_PACKAGES = {
"contrib-piohome": "^2.1.0", "contrib-piohome": "^2.3.2",
"contrib-pysite": "contrib-pysite":
"~2.%d%d.190418" % (sys.version_info[0], sys.version_info[1]), "~2.%d%d.190418" % (sys.version_info[0], sys.version_info[1]),
"tool-pioplus": "^2.5.2", "tool-pioplus": "^2.5.2",
"tool-unity": "~1.20403.0", "tool-unity": "~1.20403.0",
"tool-scons": "~2.20501.7" if PY2 else "~3.30005.0" "tool-scons": "~2.20501.7" if PY2 else "~3.30101.0"
} }
PIOPLUS_AUTO_UPDATES_MAX = 100 PIOPLUS_AUTO_UPDATES_MAX = 100

View File

@ -21,6 +21,7 @@ from glob import glob
from os.path import isdir, join from os.path import isdir, join
import click import click
import semantic_version
from platformio import app, exception, util from platformio import app, exception, util
from platformio.compat import glob_escape, string_types from platformio.compat import glob_escape, string_types
@ -166,8 +167,13 @@ class LibraryManager(BasePkgManager):
return 0 return 0
return -1 if date1 < date2 else 1 return -1 if date1 < date2 else 1
semver_spec = self.parse_semver_spec( semver_spec = None
requirements) if requirements else None try:
semver_spec = semantic_version.SimpleSpec(
requirements) if requirements else None
except ValueError:
pass
item = {} item = {}
for v in versions: for v in versions:

View File

@ -86,19 +86,18 @@ class PkgRepoMixin(object):
def max_satisfying_repo_version(self, versions, requirements=None): def max_satisfying_repo_version(self, versions, requirements=None):
item = None item = None
reqspec = None reqspec = None
if requirements: try:
try: reqspec = semantic_version.SimpleSpec(
reqspec = self.parse_semver_spec(requirements, requirements) if requirements else None
raise_exception=True) except ValueError:
except ValueError: pass
pass
for v in versions: for v in versions:
if not self.is_system_compatible(v.get("system")): if not self.is_system_compatible(v.get("system")):
continue continue
# if "platformio" in v.get("engines", {}): # if "platformio" in v.get("engines", {}):
# if PkgRepoMixin.PIO_VERSION not in self.parse_semver_spec( # if PkgRepoMixin.PIO_VERSION not in requirements.SimpleSpec(
# v['engines']['platformio'], raise_exception=True): # v['engines']['platformio']):
# continue # continue
specver = semantic_version.Version(v['version']) specver = semantic_version.Version(v['version'])
if reqspec and specver not in reqspec: if reqspec and specver not in reqspec:
@ -218,28 +217,6 @@ class PkgInstallerMixin(object):
with FileUnpacker(source_path) as fu: with FileUnpacker(source_path) as fu:
return fu.unpack(dest_dir, with_progress=False) return fu.unpack(dest_dir, with_progress=False)
@staticmethod
def parse_semver_spec(value, raise_exception=False):
try:
# 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
return None
@staticmethod @staticmethod
def parse_semver_version(value, raise_exception=False): def parse_semver_version(value, raise_exception=False):
try: try:
@ -423,8 +400,8 @@ class PkgInstallerMixin(object):
return manifest return manifest
try: try:
if requirements and not self.parse_semver_spec( if requirements and not semantic_version.SimpleSpec(
requirements, raise_exception=True).match( requirements).match(
self.parse_semver_version(manifest['version'], self.parse_semver_version(manifest['version'],
raise_exception=True)): raise_exception=True)):
continue continue
@ -557,8 +534,8 @@ class PkgInstallerMixin(object):
"Package version %s doesn't satisfy requirements %s" % "Package version %s doesn't satisfy requirements %s" %
(tmp_manifest['version'], requirements)) (tmp_manifest['version'], requirements))
try: try:
assert tmp_semver and tmp_semver in self.parse_semver_spec( assert tmp_semver and tmp_semver in semantic_version.SimpleSpec(
requirements, raise_exception=True), mismatch_error requirements), mismatch_error
except (AssertionError, ValueError): except (AssertionError, ValueError):
assert tmp_manifest['version'] == requirements, mismatch_error assert tmp_manifest['version'] == requirements, mismatch_error

View File

@ -501,7 +501,7 @@ class PlatformBase( # pylint: disable=too-many-public-methods
self.package_repositories) self.package_repositories)
# if self.engines and "platformio" in self.engines: # if self.engines and "platformio" in self.engines:
# if self.PIO_VERSION not in semantic_version.Spec( # if self.PIO_VERSION not in semantic_version.SimpleSpec(
# self.engines['platformio']): # self.engines['platformio']):
# raise exception.IncompatiblePlatform(self.name, # raise exception.IncompatiblePlatform(self.name,
# str(self.PIO_VERSION)) # str(self.PIO_VERSION))

View File

@ -16,7 +16,7 @@ import glob
import json import json
import os import os
import re import re
from os.path import expanduser, isfile from os.path import expanduser, getmtime, isfile
import click import click
@ -72,13 +72,14 @@ class ProjectConfig(object):
@staticmethod @staticmethod
def get_instance(path): def get_instance(path):
if path not in ProjectConfig._instances: mtime = getmtime(path) if isfile(path) else 0
ProjectConfig._instances[path] = ProjectConfig(path) instance = ProjectConfig._instances.get(path)
return ProjectConfig._instances[path] if instance and instance["mtime"] != mtime:
instance = None
@staticmethod if not instance:
def reset_instances(): instance = {"mtime": mtime, "config": ProjectConfig(path)}
ProjectConfig._instances = {} ProjectConfig._instances[path] = instance
return instance["config"]
def __init__(self, path, parse_extra=True, expand_interpolations=True): def __init__(self, path, parse_extra=True, expand_interpolations=True):
self.path = path self.path = path
@ -311,6 +312,9 @@ class ProjectConfig(object):
return json.dumps(result) return json.dumps(result)
def save(self, path=None): def save(self, path=None):
path = path or self.path
if path in self._instances:
del self._instances[path]
with open(path or self.path, "w") as fp: with open(path or self.path, "w") as fp:
fp.write(CONFIG_HEADER) fp.write(CONFIG_HEADER)
self._parser.write(fp) self._parser.write(fp)

View File

@ -194,20 +194,29 @@ def compute_project_checksum(config):
return checksum.hexdigest() return checksum.hexdigest()
def load_project_ide_data(project_dir, env_name): def load_project_ide_data(project_dir, envs):
from platformio.commands.run import cli as cmd_run from platformio.commands.run import cli as cmd_run
result = CliRunner().invoke(cmd_run, [ assert envs
"--project-dir", project_dir, "--environment", env_name, "--target", if not isinstance(envs, (list, tuple, set)):
"idedata" envs = [envs]
]) args = ["--project-dir", project_dir, "--target", "idedata"]
for env in envs:
args.extend(["-e", env])
result = CliRunner().invoke(cmd_run, args)
if result.exit_code != 0 and not isinstance(result.exception, if result.exit_code != 0 and not isinstance(result.exception,
exception.ReturnErrorCode): exception.ReturnErrorCode):
raise result.exception raise result.exception
if '"includes":' not in result.output: if '"includes":' not in result.output:
raise exception.PlatformioException(result.output) raise exception.PlatformioException(result.output)
data = {}
for line in result.output.split("\n"): for line in result.output.split("\n"):
line = line.strip() line = line.strip()
if line.startswith('{"') and line.endswith("}"): if (line.startswith('{"') and line.endswith("}")
return json.loads(line) and "env_name" in line):
return None _data = json.loads(line)
if "env_name" in _data:
data[_data['env_name']] = _data
if len(envs) == 1 and envs[0] in data:
return data[envs[0]]
return data or None

View File

@ -330,7 +330,8 @@ Examples are listed from `%s development platform repository <%s>`_:
examples_dir = join(p.get_dir(), "examples") examples_dir = join(p.get_dir(), "examples")
if isdir(examples_dir): if isdir(examples_dir):
for eitem in os.listdir(examples_dir): for eitem in os.listdir(examples_dir):
if not isdir(join(examples_dir, eitem)): example_dir = join(examples_dir, eitem)
if not isdir(example_dir) or not os.listdir(example_dir):
continue continue
url = "%s/tree/master/examples/%s" % (github_url, eitem) url = "%s/tree/master/examples/%s" % (github_url, eitem)
lines.append("* `%s <%s>`_" % (eitem, campaign_url(url))) lines.append("* `%s <%s>`_" % (eitem, campaign_url(url)))
@ -894,7 +895,8 @@ def update_project_examples():
examples_md_lines = [] examples_md_lines = []
if isdir(platform_examples_dir): if isdir(platform_examples_dir):
for item in os.listdir(platform_examples_dir): for item in os.listdir(platform_examples_dir):
if not isdir(join(platform_examples_dir, item)): example_dir = join(platform_examples_dir, item)
if not isdir(example_dir) or not os.listdir(example_dir):
continue continue
url = "%s/tree/master/examples/%s" % (github_url, item) url = "%s/tree/master/examples/%s" % (github_url, item)
examples_md_lines.append("* [%s](%s)" % (item, url)) examples_md_lines.append("* [%s](%s)" % (item, url))

View File

@ -23,8 +23,8 @@ install_requires = [
"colorama", "colorama",
"pyserial>=3,<4,!=3.3", "pyserial>=3,<4,!=3.3",
"requests>=2.4.0,<3", "requests>=2.4.0,<3",
"semantic_version>=2.5.0,<3", "semantic_version>=2.8.1,<3",
"tabulate>=0.8.3" "tabulate>=0.8.3,<1"
] ]
setup( setup(