diff --git a/HISTORY.rst b/HISTORY.rst index 975ddbb3..f9f1ccc3 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,6 +6,17 @@ Release Notes PlatformIO 4.0 -------------- +4.0.3 (2019-08-30) +~~~~~~~~~~~~~~~~~~ + +* Added support for multi-environment PlatformIO project for `CLion IDE `__ (`issue #2824 `_) +* Generate ``.ccls`` LSP file for `Vim `__ cross references, hierarchies, completion and semantic highlighting (`issue #2952 `_) +* Added support for `PLATFORMIO_DISABLE_COLOR `__ system environment variable which disables color ANSI-codes in a terminal output (`issue #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 `__ CLI flag does not override declared `upload_port `__ option in `"platformio.ini" (Project Configuration File) `__ + 4.0.2 (2019-08-23) ~~~~~~~~~~~~~~~~~~ diff --git a/docs b/docs index 3f5d12ca..704ff85c 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 3f5d12ca25f4af666871d3d87d191a4deb478937 +Subproject commit 704ff85c7de13079a28a7bd9a7fb2adefb071eee diff --git a/examples b/examples index a71564ab..6859117a 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit a71564ab46d27c387f17814056b659f826b7db24 +Subproject commit 6859117a8c0b5d293a00e29b339250d0587f31de diff --git a/platformio/__init__.py b/platformio/__init__.py index ee53de46..1b010521 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = (4, 0, 2) +VERSION = (4, 0, 3) __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/__main__.py b/platformio/__main__.py index d4664935..d88f69a8 100644 --- a/platformio/__main__.py +++ b/platformio/__main__.py @@ -55,13 +55,15 @@ def configure(): except (AttributeError, ImportError): pass - # handle PLATFORMIO_FORCE_COLOR - if str(os.getenv("PLATFORMIO_FORCE_COLOR", "")).lower() == "true": - try: + try: + if str(os.getenv("PLATFORMIO_DISABLE_COLOR", "")).lower() == "true": + # pylint: disable=protected-access + click._compat.isatty = lambda stream: False + elif str(os.getenv("PLATFORMIO_FORCE_COLOR", "")).lower() == "true": # pylint: disable=protected-access click._compat.isatty = lambda stream: True - except: # pylint: disable=bare-except - pass + except: # pylint: disable=bare-except + pass # Handle IOError issue with VSCode's Terminal (Windows) click_echo_origin = [click.echo, click.secho] diff --git a/platformio/builder/tools/pioide.py b/platformio/builder/tools/pioide.py index 1814f1b9..43d32404 100644 --- a/platformio/builder/tools/pioide.py +++ b/platformio/builder/tools/pioide.py @@ -140,6 +140,8 @@ def DumpIDEData(env, projenv): LINTCXXCOM = "$CXXFLAGS $CCFLAGS $CPPFLAGS" data = { + "env_name": + env['PIOENV'], "libsource_dirs": [env.subst(l) for l in env.GetLibSourceDirs()], "defines": _dump_defines(env), diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index b5ace28c..185b2985 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -300,20 +300,8 @@ class LibBuilderBase(object): ]) return items - def _validate_search_files(self, search_files=None): - if not search_files: - 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): + def _get_found_includes( # pylint: disable=too-many-branches + self, search_files=None): # all include directories if not LibBuilderBase._INCLUDE_DIRS_CACHE: LibBuilderBase._INCLUDE_DIRS_CACHE = [] @@ -326,7 +314,11 @@ class LibBuilderBase(object): include_dirs.extend(LibBuilderBase._INCLUDE_DIRS_CACHE) 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: assert "+" in self.lib_ldf_mode candidates = LibBuilderBase.CCONDITIONAL_SCANNER( @@ -334,6 +326,11 @@ class LibBuilderBase(object): self.env, tuple(include_dirs), 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 if self.verbose and "+" in self.lib_ldf_mode: sys.stderr.write( diff --git a/platformio/builder/tools/pioproject.py b/platformio/builder/tools/pioproject.py index 5797755d..614fb9bd 100644 --- a/platformio/builder/tools/pioproject.py +++ b/platformio/builder/tools/pioproject.py @@ -32,7 +32,8 @@ def GetProjectOption(env, option, default=None): def LoadProjectOptions(env): for option, value in env.GetProjectOptions(): 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 env[option_meta.buildenvvar] = value diff --git a/platformio/commands/debug/helpers.py b/platformio/commands/debug/helpers.py index 91f06318..33e1e855 100644 --- a/platformio/commands/debug/helpers.py +++ b/platformio/commands/debug/helpers.py @@ -19,7 +19,7 @@ from hashlib import sha1 from io import BytesIO from os.path import isfile -from platformio import exception, util +from platformio import exception, fs, util from platformio.commands.platform import \ platform_install as cmd_platform_install from platformio.commands.run import cli as cmd_run @@ -165,11 +165,11 @@ def configure_esp32_load_cmds(debug_options, configuration): mon_cmds = [ '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") ] 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 diff --git a/platformio/commands/debug/process.py b/platformio/commands/debug/process.py index f363ccb5..e45b6c7a 100644 --- a/platformio/commands/debug/process.py +++ b/platformio/commands/debug/process.py @@ -17,6 +17,7 @@ import signal import click from twisted.internet import protocol # pylint: disable=import-error +from platformio import fs from platformio.compat import string_types from platformio.proc import get_pythonexe_path from platformio.project.helpers import get_project_core_dir @@ -38,6 +39,10 @@ class BaseProcess(protocol.ProcessProtocol, object): _patterns = self.COMMON_PATTERNS.copy() _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): for key, value in _patterns.items(): pattern = "$%s" % key diff --git a/platformio/commands/debug/server.py b/platformio/commands/debug/server.py index 98f1e0e1..e5bc6e45 100644 --- a/platformio/commands/debug/server.py +++ b/platformio/commands/debug/server.py @@ -18,7 +18,7 @@ from os.path import isdir, isfile, join from twisted.internet import error # 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.proc import where_is_program @@ -74,7 +74,7 @@ class DebugServer(BaseProcess): str_args = " ".join( [arg if arg.startswith("-") else '"%s"' % arg for arg in 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: env = os.environ.copy() # prepend server "lib" folder to LD path diff --git a/platformio/commands/init.py b/platformio/commands/init.py index 64b5465d..e23d24e7 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -388,7 +388,6 @@ def fill_project_envs(ctx, project_dir, board_ids, project_option, env_prefix, if modified: config.save() - config.reset_instances() def _install_dependent_platforms(ctx, platforms): diff --git a/platformio/fs.py b/platformio/fs.py index a5f61ce5..1f330abe 100644 --- a/platformio/fs.py +++ b/platformio/fs.py @@ -23,7 +23,7 @@ from glob import glob import click 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): @@ -146,6 +146,12 @@ def match_src_files(src_dir, src_filter=None, src_exts=None): 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 _onerror(func, path, __): diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index afc54473..fb276af6 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -14,14 +14,13 @@ import codecs import os -import re import sys from os.path import abspath, basename, expanduser, isdir, isfile, join, relpath import bottle 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.project.config import ProjectConfig from platformio.project.helpers import (get_project_lib_dir, @@ -98,21 +97,15 @@ class ProjectGenerator(object): for key, value in tpl_vars.items(): 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"): if key not in tpl_vars: 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 - @staticmethod - def to_unix_path(path): - if not WINDOWS or not path: - return path - return re.sub(r"[\\]+", "/", path) - def get_src_files(self): result = [] with fs.cd(self.project_dir): diff --git a/platformio/ide/tpls/clion/.idea/workspace.xml.tpl b/platformio/ide/tpls/clion/.idea/workspace.xml.tpl index 8cf4214f..d9cf6a8f 100644 --- a/platformio/ide/tpls/clion/.idea/workspace.xml.tpl +++ b/platformio/ide/tpls/clion/.idea/workspace.xml.tpl @@ -15,10 +15,18 @@ - - - - + + +% envs = config.envs() +% if len(envs) > 1: +% for env in envs: + +% end + +% else: + +% end + @@ -163,10 +171,6 @@ - - - - @@ -199,17 +203,6 @@ - - - - - - - - - - - diff --git a/platformio/ide/tpls/clion/CMakeLists.txt.tpl b/platformio/ide/tpls/clion/CMakeLists.txt.tpl index 4680f6b4..c3ff24f9 100644 --- a/platformio/ide/tpls/clion/CMakeLists.txt.tpl +++ b/platformio/ide/tpls/clion/CMakeLists.txt.tpl @@ -16,49 +16,49 @@ endif() add_custom_target( PLATFORMIO_BUILD ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run + COMMAND ${PLATFORMIO_CMD} -f -c clion run "$<$>:-e${CMAKE_BUILD_TYPE}>" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target( PLATFORMIO_BUILD_VERBOSE ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --verbose + COMMAND ${PLATFORMIO_CMD} -f -c clion run --verbose "$<$>:-e${CMAKE_BUILD_TYPE}>" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target( PLATFORMIO_UPLOAD ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --target upload + COMMAND ${PLATFORMIO_CMD} -f -c clion run --target upload "$<$>:-e${CMAKE_BUILD_TYPE}>" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target( PLATFORMIO_CLEAN ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --target clean + COMMAND ${PLATFORMIO_CMD} -f -c clion run --target clean "$<$>:-e${CMAKE_BUILD_TYPE}>" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target( PLATFORMIO_MONITOR ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion device monitor + COMMAND ${PLATFORMIO_CMD} -f -c clion device monitor "$<$>:-e${CMAKE_BUILD_TYPE}>" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target( PLATFORMIO_TEST ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion test + COMMAND ${PLATFORMIO_CMD} -f -c clion test "$<$>:-e${CMAKE_BUILD_TYPE}>" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target( PLATFORMIO_PROGRAM ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --target program + COMMAND ${PLATFORMIO_CMD} -f -c clion run --target program "$<$>:-e${CMAKE_BUILD_TYPE}>" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target( PLATFORMIO_UPLOADFS ALL - COMMAND ${PLATFORMIO_CMD} -f -c clion run --target uploadfs + COMMAND ${PLATFORMIO_CMD} -f -c clion run --target uploadfs "$<$>:-e${CMAKE_BUILD_TYPE}>" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) @@ -74,4 +74,10 @@ add_custom_target( 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}) diff --git a/platformio/ide/tpls/clion/CMakeListsPrivate.txt.tpl b/platformio/ide/tpls/clion/CMakeListsPrivate.txt.tpl index 5d69c4d8..7967881e 100644 --- a/platformio/ide/tpls/clion/CMakeListsPrivate.txt.tpl +++ b/platformio/ide/tpls/clion/CMakeListsPrivate.txt.tpl @@ -5,6 +5,8 @@ # please create `CMakeListsUser.txt` in the root of project. # The `CMakeListsUser.txt` will not be overwritten by PlatformIO. +%from platformio.project.helpers import (load_project_ide_data) +% % import re % % def _normalize_path(path): @@ -19,6 +21,14 @@ % end % return path % 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) }}") @@ -37,12 +47,31 @@ SET(CMAKE_C_STANDARD {{ cc_stds[-1] }}) set(CMAKE_CXX_STANDARD {{ cxx_stds[-1] }}) % end -% for define in defines: -add_definitions(-D'{{!re.sub(r"([\"\(\)#])", r"\\\1", define)}}') -% end +if (CMAKE_BUILD_TYPE MATCHES "{{ env_name }}") +%for define in defines: + add_definitions(-D'{{!re.sub(r"([\"\(\)#])", r"\\\1", define)}}') +%end -% for include in includes: -include_directories("{{ _normalize_path(include) }}") -% end +%for include in includes: + include_directories("{{ _normalize_path(to_unix_path(include)) }}") +%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) }}/*.*") diff --git a/platformio/ide/tpls/vim/.ccls.tpl b/platformio/ide/tpls/vim/.ccls.tpl new file mode 100644 index 00000000..ad22fce7 --- /dev/null +++ b/platformio/ide/tpls/vim/.ccls.tpl @@ -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 diff --git a/platformio/managers/core.py b/platformio/managers/core.py index 74d8afe3..b9d5a648 100644 --- a/platformio/managers/core.py +++ b/platformio/managers/core.py @@ -24,12 +24,12 @@ from platformio.proc import copy_pythonpath_to_osenv, get_pythonexe_path from platformio.project.helpers import get_project_packages_dir CORE_PACKAGES = { - "contrib-piohome": "^2.1.0", + "contrib-piohome": "^2.3.2", "contrib-pysite": "~2.%d%d.190418" % (sys.version_info[0], sys.version_info[1]), "tool-pioplus": "^2.5.2", "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 diff --git a/platformio/managers/lib.py b/platformio/managers/lib.py index b7fc8e51..3f46be61 100644 --- a/platformio/managers/lib.py +++ b/platformio/managers/lib.py @@ -21,6 +21,7 @@ from glob import glob from os.path import isdir, join import click +import semantic_version from platformio import app, exception, util from platformio.compat import glob_escape, string_types @@ -166,8 +167,13 @@ class LibraryManager(BasePkgManager): return 0 return -1 if date1 < date2 else 1 - semver_spec = self.parse_semver_spec( - requirements) if requirements else None + semver_spec = None + try: + semver_spec = semantic_version.SimpleSpec( + requirements) if requirements else None + except ValueError: + pass + item = {} for v in versions: diff --git a/platformio/managers/package.py b/platformio/managers/package.py index 9d99b1a3..f84ba472 100644 --- a/platformio/managers/package.py +++ b/platformio/managers/package.py @@ -86,19 +86,18 @@ class PkgRepoMixin(object): def max_satisfying_repo_version(self, versions, requirements=None): item = None reqspec = None - if requirements: - try: - reqspec = self.parse_semver_spec(requirements, - raise_exception=True) - except ValueError: - pass + try: + reqspec = semantic_version.SimpleSpec( + requirements) if requirements else None + except ValueError: + pass for v in versions: if not self.is_system_compatible(v.get("system")): continue # if "platformio" in v.get("engines", {}): - # if PkgRepoMixin.PIO_VERSION not in self.parse_semver_spec( - # v['engines']['platformio'], raise_exception=True): + # if PkgRepoMixin.PIO_VERSION not in requirements.SimpleSpec( + # v['engines']['platformio']): # continue specver = semantic_version.Version(v['version']) if reqspec and specver not in reqspec: @@ -218,28 +217,6 @@ class PkgInstallerMixin(object): with FileUnpacker(source_path) as fu: 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 def parse_semver_version(value, raise_exception=False): try: @@ -423,8 +400,8 @@ class PkgInstallerMixin(object): return manifest try: - if requirements and not self.parse_semver_spec( - requirements, raise_exception=True).match( + if requirements and not semantic_version.SimpleSpec( + requirements).match( self.parse_semver_version(manifest['version'], raise_exception=True)): continue @@ -557,8 +534,8 @@ class PkgInstallerMixin(object): "Package version %s doesn't satisfy requirements %s" % (tmp_manifest['version'], requirements)) try: - assert tmp_semver and tmp_semver in self.parse_semver_spec( - requirements, raise_exception=True), mismatch_error + assert tmp_semver and tmp_semver in semantic_version.SimpleSpec( + requirements), mismatch_error except (AssertionError, ValueError): assert tmp_manifest['version'] == requirements, mismatch_error diff --git a/platformio/managers/platform.py b/platformio/managers/platform.py index 996d7162..dafdb855 100644 --- a/platformio/managers/platform.py +++ b/platformio/managers/platform.py @@ -501,7 +501,7 @@ class PlatformBase( # pylint: disable=too-many-public-methods self.package_repositories) # 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']): # raise exception.IncompatiblePlatform(self.name, # str(self.PIO_VERSION)) diff --git a/platformio/project/config.py b/platformio/project/config.py index e98ace55..6ca36a14 100644 --- a/platformio/project/config.py +++ b/platformio/project/config.py @@ -16,7 +16,7 @@ import glob import json import os import re -from os.path import expanduser, isfile +from os.path import expanduser, getmtime, isfile import click @@ -72,13 +72,14 @@ class ProjectConfig(object): @staticmethod def get_instance(path): - if path not in ProjectConfig._instances: - ProjectConfig._instances[path] = ProjectConfig(path) - return ProjectConfig._instances[path] - - @staticmethod - def reset_instances(): - ProjectConfig._instances = {} + mtime = getmtime(path) if isfile(path) else 0 + instance = ProjectConfig._instances.get(path) + if instance and instance["mtime"] != mtime: + instance = None + if not instance: + instance = {"mtime": mtime, "config": ProjectConfig(path)} + ProjectConfig._instances[path] = instance + return instance["config"] def __init__(self, path, parse_extra=True, expand_interpolations=True): self.path = path @@ -311,6 +312,9 @@ class ProjectConfig(object): return json.dumps(result) 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: fp.write(CONFIG_HEADER) self._parser.write(fp) diff --git a/platformio/project/helpers.py b/platformio/project/helpers.py index 0be42893..7ead95eb 100644 --- a/platformio/project/helpers.py +++ b/platformio/project/helpers.py @@ -194,20 +194,29 @@ def compute_project_checksum(config): 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 - result = CliRunner().invoke(cmd_run, [ - "--project-dir", project_dir, "--environment", env_name, "--target", - "idedata" - ]) + assert envs + if not isinstance(envs, (list, tuple, set)): + 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, exception.ReturnErrorCode): raise result.exception if '"includes":' not in result.output: raise exception.PlatformioException(result.output) + data = {} for line in result.output.split("\n"): line = line.strip() - if line.startswith('{"') and line.endswith("}"): - return json.loads(line) - return None + if (line.startswith('{"') and line.endswith("}") + and "env_name" in line): + _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 diff --git a/scripts/docspregen.py b/scripts/docspregen.py index 0a3cf437..dc18c5a7 100644 --- a/scripts/docspregen.py +++ b/scripts/docspregen.py @@ -330,7 +330,8 @@ Examples are listed from `%s development platform repository <%s>`_: examples_dir = join(p.get_dir(), "examples") if isdir(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 url = "%s/tree/master/examples/%s" % (github_url, eitem) lines.append("* `%s <%s>`_" % (eitem, campaign_url(url))) @@ -894,7 +895,8 @@ def update_project_examples(): examples_md_lines = [] if isdir(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 url = "%s/tree/master/examples/%s" % (github_url, item) examples_md_lines.append("* [%s](%s)" % (item, url)) diff --git a/setup.py b/setup.py index 66e0f9c8..7d37ad42 100644 --- a/setup.py +++ b/setup.py @@ -23,8 +23,8 @@ install_requires = [ "colorama", "pyserial>=3,<4,!=3.3", "requests>=2.4.0,<3", - "semantic_version>=2.5.0,<3", - "tabulate>=0.8.3" + "semantic_version>=2.8.1,<3", + "tabulate>=0.8.3,<1" ] setup(