mirror of
https://github.com/platformio/platformio-core.git
synced 2025-07-30 01:57:13 +02:00
Merge branch 'release/v3.5.0'
This commit is contained in:
@ -20,7 +20,8 @@ matrix:
|
||||
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo pip install -U tox; else pip install -U tox; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then curl -fsSL https://bootstrap.pypa.io/get-pip.py | sudo python; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo pip install tox; else pip install -U tox; fi
|
||||
|
||||
# ChipKIT issue: install 32-bit support for GCC PIC32
|
||||
- if [[ "$TOX_ENV" == "py27" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install libc6-i386; fi
|
||||
|
91
HISTORY.rst
91
HISTORY.rst
@ -4,13 +4,82 @@ Release Notes
|
||||
PlatformIO 3.0
|
||||
--------------
|
||||
|
||||
3.5.0 (2017-12-28)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* `PlatformIO Home <http://docs.platformio.org/page/home/index.html>`__ -
|
||||
interact with PlatformIO ecosystem using modern and cross-platform GUI:
|
||||
|
||||
- Library Manager:
|
||||
|
||||
* Search for new libraries in PlatformIO Registry
|
||||
* "1-click" library installation, per-project libraries, extra storages
|
||||
* List installed libraries in multiple storages
|
||||
* List built-in libraries (by frameworks)
|
||||
* Updates for installed libraries
|
||||
* Multiple examples, trending libraries, and more.
|
||||
|
||||
- PlatformIO Projects
|
||||
- PIO Account
|
||||
- Development platforms, frameworks and board explorer
|
||||
- Device Manager: serial, logical, and multicast DNS services
|
||||
|
||||
* Integration with `Jenkins CI <http://docs.platformio.org/page/ci/jenkins.html>`_
|
||||
* New `include <http://docs.platformio.org/page/projectconf/section_platformio.html#include-dir>`__
|
||||
folder for project's header files
|
||||
(`issue #1107 <https://github.com/platformio/platformio-core/issues/1107>`_)
|
||||
* Depend on development platform using VSC URL (Git, Mercurial and Subversion)
|
||||
instead of a name in `Project Configuration File "platformio.ini" <http://docs.platformio.org/page/projectconf/section_env_general.html#platform>`__.
|
||||
Drop support for ``*_stage`` dev/platform names (use VCS URL instead).
|
||||
* Reinstall/redownload package with a new ``-f, --force`` option for
|
||||
`platformio lib install <http://docs.platformio.org/page/userguide/lib/cmd_install.html>`__
|
||||
and `platformio platform install <http://docs.platformio.org/page/userguide/platforms/cmd_install.html>`__
|
||||
commands
|
||||
(`issue #778 <https://github.com/platformio/platformio-core/issues/778>`_)
|
||||
* Handle missed dependencies and provide a solution based on PlatformIO Library
|
||||
Registry
|
||||
(`issue #781 <https://github.com/platformio/platformio-core/issues/781>`_)
|
||||
* New setting `projects_dir <http://docs.platformio.org/page/userguide/cmd_settings.html#projects-dir>`__
|
||||
that allows to override a default PIO Home Projects location
|
||||
(`issue #1161 <https://github.com/platformio/platformio-core/issues/1161>`_)
|
||||
|
||||
* `Library Dependency Finder (LDF) <http://docs.platformio.org/page/librarymanager/ldf.html>`__:
|
||||
|
||||
- Search for dependencies used in `PIO Unit Testing <http://docs.platformio.org/page/plus/unit-testing.html>`__
|
||||
(`issue #953 <https://github.com/platformio/platformio-core/issues/953>`_)
|
||||
- Parse library source file in pair with a header when they have the same name
|
||||
(`issue #1175 <https://github.com/platformio/platformio-core/issues/1175>`_)
|
||||
- Handle library dependencies defined as VCS or SemVer in
|
||||
`Project Configuration File "platformio.ini" <http://docs.platformio.org/page/projectconf/section_env_general.html#platform>`__
|
||||
(`issue #1155 <https://github.com/platformio/platformio-core/issues/1155>`_)
|
||||
- Added option to configure library `Compatible Mode <http://docs.platformio.org/page/librarymanager/ldf.html#compatibility-mode>`__
|
||||
using `library.json <http://docs.platformio.org/page/librarymanager/config.html>`__
|
||||
|
||||
* New options for `platformio device list <http://docs.platformio.org/page/userguide/cmd_device.html#platformio-device-list>`__
|
||||
command:
|
||||
|
||||
- ``--serial`` list available serial ports (default)
|
||||
- ``--logical`` list logical devices
|
||||
- ``--mdns`` discover multicast DNS services
|
||||
(`issue #463 <https://github.com/platformio/platformio-core/issues/463>`_)
|
||||
|
||||
* Fixed platforms, packages, and libraries updating behind proxy
|
||||
(`issue #1061 <https://github.com/platformio/platformio-core/issues/1061>`_)
|
||||
* Fixed missing toolchain include paths for project generator
|
||||
(`issue #1154 <https://github.com/platformio/platformio-core/issues/1154>`_)
|
||||
* Fixed "Super-Quick (Mac / Linux)" installation in "get-platformio.py" script
|
||||
(`issue #1017 <https://github.com/platformio/platformio-core/issues/1017>`_)
|
||||
* Fixed "get-platformio.py" script which hangs on Windows 10
|
||||
(`issue #1118 <https://github.com/platformio/platformio-core/issues/1118>`_)
|
||||
* Other bug fixes and performance improvements
|
||||
|
||||
3.4.1 (2017-08-02)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Pre/Post extra scripting for advanced control of PIO Build System
|
||||
(`issue #891 <https://github.com/platformio/platformio-core/issues/891>`_)
|
||||
* New `lib_archive <http://docs.platformio.org/page/projectconf/section_env_library.html#lib-archive>`_
|
||||
option to control library archiving and linking behaviour
|
||||
option to control library archiving and linking behavior
|
||||
(`issue #993 <https://github.com/platformio/platformio-core/issues/993>`_)
|
||||
* Add "inc" folder automatically to CPPPATH when "src" is available (works for project and library)
|
||||
(`issue #1003 <https://github.com/platformio/platformio-core/issues/1003>`_)
|
||||
@ -104,7 +173,7 @@ PlatformIO 3.0
|
||||
command
|
||||
(`issue #430 <https://github.com/platformio/platformio-core/issues/430>`_)
|
||||
* List supported frameworks, SDKs with a new
|
||||
`pio platform frameworks <http://docs.platformio.org/page/userguide/platforms/cmd_frameworks.htmll>`__ command
|
||||
`pio platform frameworks <http://docs.platformio.org/page/userguide/platforms/cmd_frameworks.html>`__ command
|
||||
* Visual Studio Code extension for PlatformIO
|
||||
(`issue #619 <https://github.com/platformio/platformio-core/issues/619>`_)
|
||||
* Added new options ``--no-reset``, ``--monitor-rts`` and ``--monitor-dtr``
|
||||
@ -222,7 +291,7 @@ PlatformIO 3.0
|
||||
|
||||
* `PlatformIO Plus <https://pioplus.com>`__
|
||||
|
||||
+ Local and Embedded `Unit Testing <http://docs.platformio.org/page/unit_testing.html>`__
|
||||
+ Local and Embedded `Unit Testing <http://docs.platformio.org/page/plus/unit-testing.html>`__
|
||||
(`issue #408 <https://github.com/platformio/platformio-core/issues/408>`_,
|
||||
`issue #519 <https://github.com/platformio/platformio-core/issues/519>`_)
|
||||
|
||||
@ -893,7 +962,7 @@ PlatformIO 2.0
|
||||
`windows_x86 <http://docs.platformio.org/page/platforms/windows_x86.html>`__
|
||||
development platforms
|
||||
(`issue #263 <https://github.com/platformio/platformio-core/issues/263>`_)
|
||||
* Added `PlatformIO Demo <http://docs.platformio.org/page/demo.html>`_
|
||||
* Added `PlatformIO Demo <http://docs.platformio.org/page/userguide/demo.html>`_
|
||||
page to documentation
|
||||
* Simplified `installation <http://docs.platformio.org/page/installation.html>`__
|
||||
process of PlatformIO
|
||||
@ -1252,7 +1321,7 @@ PlatformIO 1.0
|
||||
(`issue #83 <https://github.com/platformio/platformio-core/issues/83>`_)
|
||||
* Added ``--json-output`` option to
|
||||
`platformio boards <http://docs.platformio.org/page/userguide/cmd_boards.html>`__
|
||||
and `platformio search <http://docs.platformio.org/page/userguide/cmd_search.html>`__
|
||||
and `platformio search <http://docs.platformio.org/page/userguide/platforms/cmd_search.html>`__
|
||||
commands which allows to return the output in `JSON <http://en.wikipedia.org/wiki/JSON>`_ format
|
||||
(`issue #42 <https://github.com/platformio/platformio-core/issues/42>`_)
|
||||
* Allowed to ignore some libs from *Library Dependency Finder* via
|
||||
@ -1293,7 +1362,7 @@ PlatformIO 0.0
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Added ``--json-output`` option to
|
||||
`platformio list <http://docs.platformio.org/page/userguide/cmd_list.html>`__,
|
||||
`platformio list <http://docs.platformio.org/page/userguide/platforms/cmd_list.htmll>`__,
|
||||
`platformio serialports list <http://docs.platformio.org/page/userguide/cmd_serialports.html>`__ and
|
||||
`platformio lib list <http://docs.platformio.org/page/userguide/lib/cmd_list.html>`__
|
||||
commands which allows to return the output in `JSON <http://en.wikipedia.org/wiki/JSON>`_ format
|
||||
@ -1337,14 +1406,14 @@ PlatformIO 0.0
|
||||
|
||||
* Ask user to install platform (when it hasn't been installed yet) within
|
||||
`platformio run <http://docs.platformio.org/page/userguide/cmd_run.html>`__
|
||||
and `platformio show <http://docs.platformio.org/page/userguide/cmd_show.html>`_ commands
|
||||
and `platformio show <http://docs.platformio.org/page/userguide/platforms/cmd_show.html>`_ commands
|
||||
* Improved main `documentation <http://docs.platformio.org>`_
|
||||
* Fixed "*OSError: [Errno 2] No such file or directory*" within
|
||||
`platformio run <http://docs.platformio.org/page/userguide/cmd_run.html>`__
|
||||
command when PlatformIO isn't installed properly
|
||||
* Fixed example for `Eclipse IDE with Tiva board <https://github.com/platformio/platformio-examples/tree/develop/ide/eclipse>`_
|
||||
* Fixed example for Eclipse IDE with Tiva board
|
||||
(`issue #32 <https://github.com/platformio/platformio-core/pull/32>`_)
|
||||
* Upgraded `Eclipse Project Examples <https://github.com/platformio/platformio-examples/tree/develop/ide/eclipse>`_
|
||||
* Upgraded Eclipse Project Examples
|
||||
to latest *Luna* and *PlatformIO* releases
|
||||
|
||||
0.9.0 (2014-12-01)
|
||||
@ -1433,7 +1502,7 @@ PlatformIO 0.0
|
||||
* Implemented (especially for `SmartAnthill <http://docs.smartanthill.ikravets.com/>`_)
|
||||
`platformio run -t uploadlazy <http://docs.platformio.org/page/userguide/cmd_run.html>`_
|
||||
target (no dependencies to framework libs, ELF and etc.)
|
||||
* Allowed to skip default packages via `platformio install --skip-default-package <http://docs.platformio.org/page/userguide/cmd_install.html#cmdoption--skip-default>`_
|
||||
* Allowed to skip default packages via `platformio install --skip-default-package <http://docs.platformio.org/page/userguide/platforms/cmd_install.html#cmdoption-platformio-platform-install-skip-default>`_
|
||||
option
|
||||
* Added tools for *Raspberry Pi* platform
|
||||
* Added support for *Microduino* and *Raspduino* boards in
|
||||
@ -1442,7 +1511,7 @@ PlatformIO 0.0
|
||||
0.3.1 (2014-06-21)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Fixed auto-installer for Windows OS (bug with %PATH% customisations)
|
||||
* Fixed auto-installer for Windows OS (bug with %PATH% custom installation)
|
||||
|
||||
|
||||
0.3.0 (2014-06-21)
|
||||
|
5
Makefile
5
Makefile
@ -9,7 +9,10 @@ isort:
|
||||
yapf:
|
||||
yapf --recursive --in-place platformio/
|
||||
|
||||
before-commit: isort yapf lint
|
||||
test:
|
||||
py.test -v -s tests --ignore tests/test_examples.py --ignore tests/test_pkgmanifest.py
|
||||
|
||||
before-commit: isort yapf lint test
|
||||
|
||||
clean-docs:
|
||||
rm -rf docs/_build
|
||||
|
2
docs
2
docs
Submodule docs updated: ebd68b4bac...c76ccaf337
2
examples
2
examples
Submodule examples updated: a2d7ba27c3...2d716306f3
@ -14,7 +14,7 @@
|
||||
|
||||
import sys
|
||||
|
||||
VERSION = (3, 4, 1)
|
||||
VERSION = (3, 5, 0)
|
||||
__version__ = ".".join([str(s) for s in VERSION])
|
||||
|
||||
__title__ = "platformio"
|
||||
|
@ -80,29 +80,45 @@ def process_result(ctx, result, force, caller): # pylint: disable=W0613
|
||||
maintenance.on_platformio_end(ctx, result)
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
if "cygwin" in system().lower():
|
||||
raise exception.CygwinEnvDetected()
|
||||
def configure():
|
||||
if "cygwin" in system().lower():
|
||||
raise exception.CygwinEnvDetected()
|
||||
|
||||
# https://urllib3.readthedocs.org
|
||||
# /en/latest/security.html#insecureplatformwarning
|
||||
# https://urllib3.readthedocs.org
|
||||
# /en/latest/security.html#insecureplatformwarning
|
||||
try:
|
||||
import urllib3
|
||||
urllib3.disable_warnings()
|
||||
except (AttributeError, ImportError):
|
||||
pass
|
||||
|
||||
# handle PLATFORMIO_FORCE_COLOR
|
||||
if str(os.getenv("PLATFORMIO_FORCE_COLOR", "")).lower() == "true":
|
||||
try:
|
||||
import urllib3
|
||||
urllib3.disable_warnings()
|
||||
except (AttributeError, ImportError):
|
||||
# pylint: disable=protected-access
|
||||
click._compat.isatty = lambda stream: True
|
||||
except: # pylint: disable=bare-except
|
||||
pass
|
||||
|
||||
# handle PLATFORMIO_FORCE_COLOR
|
||||
if str(os.getenv("PLATFORMIO_FORCE_COLOR", "")).lower() == "true":
|
||||
try:
|
||||
# pylint: disable=protected-access
|
||||
click._compat.isatty = lambda stream: True
|
||||
except: # pylint: disable=bare-except
|
||||
pass
|
||||
# Handle IOError issue with VSCode's Terminal (Windows)
|
||||
click_echo_origin = [click.echo, click.secho]
|
||||
|
||||
def _safe_echo(origin, *args, **kwargs):
|
||||
try:
|
||||
click_echo_origin[origin](*args, **kwargs)
|
||||
except IOError:
|
||||
(sys.stderr.write if kwargs.get("err") else
|
||||
sys.stdout.write)("%s\n" % (args[0] if args else ""))
|
||||
|
||||
click.echo = lambda *args, **kwargs: _safe_echo(0, *args, **kwargs)
|
||||
click.secho = lambda *args, **kwargs: _safe_echo(1, *args, **kwargs)
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
configure()
|
||||
cli(None, None, None)
|
||||
except Exception as e: # pylint: disable=W0703
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
if not isinstance(e, exception.ReturnErrorCode):
|
||||
maintenance.on_platformio_exception(e)
|
||||
error_str = "Error: "
|
||||
|
@ -18,16 +18,33 @@ import os
|
||||
import uuid
|
||||
from copy import deepcopy
|
||||
from os import environ, getenv, listdir, remove
|
||||
from os.path import dirname, getmtime, isdir, isfile, join
|
||||
from os.path import abspath, dirname, expanduser, getmtime, isdir, isfile, join
|
||||
from time import time
|
||||
|
||||
import requests
|
||||
from lockfile import LockFailed, LockFile
|
||||
|
||||
from platformio import __version__, exception, util
|
||||
from platformio.exception import InvalidSettingName, InvalidSettingValue
|
||||
|
||||
|
||||
def projects_dir_validate(projects_dir):
|
||||
assert isdir(projects_dir)
|
||||
return abspath(projects_dir)
|
||||
|
||||
|
||||
DEFAULT_SETTINGS = {
|
||||
"auto_update_libraries": {
|
||||
"description": "Automatically update libraries (Yes/No)",
|
||||
"value": False
|
||||
},
|
||||
"auto_update_platforms": {
|
||||
"description": "Automatically update platforms (Yes/No)",
|
||||
"value": False
|
||||
},
|
||||
"check_libraries_interval": {
|
||||
"description": "Check for the library updates interval (days)",
|
||||
"value": 7
|
||||
},
|
||||
"check_platformio_interval": {
|
||||
"description": "Check for the new PlatformIO interval (days)",
|
||||
"value": 3
|
||||
@ -36,37 +53,30 @@ DEFAULT_SETTINGS = {
|
||||
"description": "Check for the platform updates interval (days)",
|
||||
"value": 7
|
||||
},
|
||||
"check_libraries_interval": {
|
||||
"description": "Check for the library updates interval (days)",
|
||||
"value": 7
|
||||
},
|
||||
"auto_update_platforms": {
|
||||
"description": "Automatically update platforms (Yes/No)",
|
||||
"value": False
|
||||
},
|
||||
"auto_update_libraries": {
|
||||
"description": "Automatically update libraries (Yes/No)",
|
||||
"value": False
|
||||
},
|
||||
"force_verbose": {
|
||||
"description": "Force verbose output when processing environments",
|
||||
"value": False
|
||||
"enable_cache": {
|
||||
"description": "Enable caching for API requests and Library Manager",
|
||||
"value": True
|
||||
},
|
||||
"enable_ssl": {
|
||||
"description": "Enable SSL for PlatformIO Services",
|
||||
"value": False
|
||||
},
|
||||
"enable_cache": {
|
||||
"description": "Enable caching for API requests and Library Manager",
|
||||
"value": True
|
||||
},
|
||||
"enable_telemetry": {
|
||||
"description":
|
||||
("Telemetry service <http://docs.platformio.org/page/"
|
||||
"userguide/cmd_settings.html?#enable-telemetry> (Yes/No)"),
|
||||
"value":
|
||||
True
|
||||
}
|
||||
},
|
||||
"force_verbose": {
|
||||
"description": "Force verbose output when processing environments",
|
||||
"value": False
|
||||
},
|
||||
"projects_dir": {
|
||||
"description": "Default location for PlatformIO projects (PIO Home)",
|
||||
"value": join(expanduser("~"), "Documents", "PlatformIO", "Projects"),
|
||||
"validator": projects_dir_validate
|
||||
},
|
||||
}
|
||||
|
||||
SESSION_VARS = {"command_ctx": None, "force_option": False, "caller_id": None}
|
||||
@ -95,11 +105,14 @@ class State(object):
|
||||
|
||||
def __exit__(self, type_, value, traceback):
|
||||
if self._prev_state != self._state:
|
||||
with open(self.path, "w") as fp:
|
||||
if "dev" in __version__:
|
||||
json.dump(self._state, fp, indent=4)
|
||||
else:
|
||||
json.dump(self._state, fp)
|
||||
try:
|
||||
with open(self.path, "w") as fp:
|
||||
if "dev" in __version__:
|
||||
json.dump(self._state, fp, indent=4)
|
||||
else:
|
||||
json.dump(self._state, fp)
|
||||
except IOError:
|
||||
raise exception.HomeDirPermissionsError(util.get_home_dir())
|
||||
self._unlock_state_file()
|
||||
|
||||
def _lock_state_file(self):
|
||||
@ -114,13 +127,7 @@ class State(object):
|
||||
try:
|
||||
self._lockfile.acquire()
|
||||
except LockFailed:
|
||||
raise exception.PlatformioException(
|
||||
"The directory `{0}` or its parent directory is not owned by "
|
||||
"the current user and PlatformIO can not store configuration "
|
||||
"data. \nPlease check the permissions and owner of that "
|
||||
"directory. Otherwise, please remove manually `{0}` "
|
||||
"directory and PlatformIO will create new from the current "
|
||||
"user.".format(dirname(self.path)))
|
||||
raise exception.HomeDirPermissionsError(dirname(self.path))
|
||||
|
||||
def _unlock_state_file(self):
|
||||
if self._lockfile:
|
||||
@ -134,16 +141,10 @@ class ContentCache(object):
|
||||
self._db_path = None
|
||||
self._lockfile = None
|
||||
|
||||
if not get_setting("enable_cache"):
|
||||
return
|
||||
|
||||
self.cache_dir = cache_dir or join(util.get_home_dir(), ".cache")
|
||||
self._db_path = join(self.cache_dir, "db.data")
|
||||
|
||||
def __enter__(self):
|
||||
if not self._db_path or not isfile(self._db_path):
|
||||
return self
|
||||
|
||||
self.delete()
|
||||
return self
|
||||
|
||||
@ -155,6 +156,7 @@ class ContentCache(object):
|
||||
os.makedirs(self.cache_dir)
|
||||
self._lockfile = LockFile(self.cache_dir)
|
||||
if self._lockfile.is_locked() and \
|
||||
isfile(self._lockfile.lock_file) and \
|
||||
(time() - getmtime(self._lockfile.lock_file)) > 10:
|
||||
self._lockfile.break_lock()
|
||||
|
||||
@ -192,11 +194,13 @@ class ContentCache(object):
|
||||
return data
|
||||
|
||||
def set(self, key, data, valid):
|
||||
if not get_setting("enable_cache"):
|
||||
return False
|
||||
cache_path = self.get_cache_path(key)
|
||||
if isfile(cache_path):
|
||||
self.delete(key)
|
||||
if not data:
|
||||
return
|
||||
return False
|
||||
if not isdir(self.cache_dir):
|
||||
os.makedirs(self.cache_dir)
|
||||
tdmap = {"s": 1, "m": 60, "h": 3600, "d": 86400}
|
||||
@ -220,6 +224,8 @@ class ContentCache(object):
|
||||
|
||||
def delete(self, keys=None):
|
||||
""" Keys=None, delete expired items """
|
||||
if not isfile(self._db_path):
|
||||
return None
|
||||
if not keys:
|
||||
keys = []
|
||||
if not isinstance(keys, list):
|
||||
@ -266,19 +272,19 @@ def clean_cache():
|
||||
|
||||
def sanitize_setting(name, value):
|
||||
if name not in DEFAULT_SETTINGS:
|
||||
raise InvalidSettingName(name)
|
||||
raise exception.InvalidSettingName(name)
|
||||
|
||||
defdata = DEFAULT_SETTINGS[name]
|
||||
try:
|
||||
if "validator" in defdata:
|
||||
value = defdata['validator']()
|
||||
value = defdata['validator'](value)
|
||||
elif isinstance(defdata['value'], bool):
|
||||
if not isinstance(value, bool):
|
||||
value = str(value).lower() in ("true", "yes", "y", "1")
|
||||
elif isinstance(defdata['value'], int):
|
||||
value = int(value)
|
||||
except Exception:
|
||||
raise InvalidSettingValue(value, name)
|
||||
raise exception.InvalidSettingValue(value, name)
|
||||
return value
|
||||
|
||||
|
||||
@ -354,7 +360,9 @@ def get_cid():
|
||||
except: # pylint: disable=bare-except
|
||||
pass
|
||||
cid = str(
|
||||
uuid.UUID(bytes=hashlib.md5(str(_uid if _uid else uuid.getnode()))
|
||||
.digest()))
|
||||
set_state_item("cid", cid)
|
||||
uuid.UUID(
|
||||
bytes=hashlib.md5(str(_uid if _uid else uuid.getnode()))
|
||||
.digest()))
|
||||
if "windows" in util.get_systype() or os.getuid() > 0:
|
||||
set_state_item("cid", cid)
|
||||
return cid
|
||||
|
@ -16,7 +16,7 @@ import base64
|
||||
import json
|
||||
import sys
|
||||
from os import environ
|
||||
from os.path import join
|
||||
from os.path import expanduser, join
|
||||
from time import time
|
||||
|
||||
from SCons.Script import (ARGUMENTS, COMMAND_LINE_TARGETS, DEFAULT_TARGETS,
|
||||
@ -87,6 +87,7 @@ DEFAULT_ENV_OPTIONS = dict(
|
||||
UNIX_TIME=int(time()),
|
||||
PIOHOME_DIR=util.get_home_dir(),
|
||||
PROJECT_DIR=util.get_project_dir(),
|
||||
PROJECTINCLUDE_DIR=util.get_projectinclude_dir(),
|
||||
PROJECTSRC_DIR=util.get_projectsrc_dir(),
|
||||
PROJECTTEST_DIR=util.get_projecttest_dir(),
|
||||
PROJECTDATA_DIR=util.get_projectdata_dir(),
|
||||
@ -138,9 +139,13 @@ for var in ("BUILD_FLAGS", "SRC_BUILD_FLAGS", "SRC_FILTER", "EXTRA_SCRIPTS",
|
||||
|
||||
# Configure extra library source directories for LDF
|
||||
if util.get_project_optional_dir("lib_extra_dirs"):
|
||||
env.Prepend(LIBSOURCE_DIRS=util.parse_conf_multi_values(
|
||||
util.get_project_optional_dir("lib_extra_dirs")))
|
||||
env.Prepend(
|
||||
LIBSOURCE_DIRS=util.parse_conf_multi_values(
|
||||
util.get_project_optional_dir("lib_extra_dirs")))
|
||||
env.Prepend(LIBSOURCE_DIRS=env.get("LIB_EXTRA_DIRS", []))
|
||||
env['LIBSOURCE_DIRS'] = [
|
||||
expanduser(d) if d.startswith("~") else d for d in env['LIBSOURCE_DIRS']
|
||||
]
|
||||
|
||||
env.LoadPioPlatform(commonvars)
|
||||
|
||||
@ -167,7 +172,8 @@ if "envdump" in COMMAND_LINE_TARGETS:
|
||||
|
||||
if "idedata" in COMMAND_LINE_TARGETS:
|
||||
try:
|
||||
print "\n%s\n" % json.dumps(env.DumpIDEData())
|
||||
print "\n%s\n" % util.path_to_unicode(
|
||||
json.dumps(env.DumpIDEData(), ensure_ascii=False))
|
||||
env.Exit(0)
|
||||
except UnicodeDecodeError:
|
||||
sys.stderr.write(
|
||||
|
@ -15,6 +15,7 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from glob import glob
|
||||
from os import environ
|
||||
from os.path import join
|
||||
|
||||
from SCons.Defaults import processDefines
|
||||
@ -23,7 +24,7 @@ from platformio import util
|
||||
from platformio.managers.core import get_core_package_dir
|
||||
|
||||
|
||||
def dump_includes(env):
|
||||
def _dump_includes(env):
|
||||
includes = []
|
||||
|
||||
for item in env.get("CPPPATH", []):
|
||||
@ -31,7 +32,7 @@ def dump_includes(env):
|
||||
|
||||
# installed libs
|
||||
for lb in env.GetLibBuilders():
|
||||
includes.extend(lb.get_inc_dirs())
|
||||
includes.extend(lb.get_include_dirs())
|
||||
|
||||
# includes from toolchains
|
||||
p = env.PioPlatform()
|
||||
@ -41,6 +42,8 @@ def dump_includes(env):
|
||||
toolchain_dir = util.glob_escape(p.get_package_dir(name))
|
||||
toolchain_incglobs = [
|
||||
join(toolchain_dir, "*", "include*"),
|
||||
join(toolchain_dir, "*", "include", "c++", "*"),
|
||||
join(toolchain_dir, "*", "include", "c++", "*", "*-*-*"),
|
||||
join(toolchain_dir, "lib", "gcc", "*", "*", "include*")
|
||||
]
|
||||
for g in toolchain_incglobs:
|
||||
@ -53,7 +56,29 @@ def dump_includes(env):
|
||||
return includes
|
||||
|
||||
|
||||
def dump_defines(env):
|
||||
def _get_gcc_defines(env):
|
||||
items = []
|
||||
try:
|
||||
sysenv = environ.copy()
|
||||
sysenv['PATH'] = str(env['ENV']['PATH'])
|
||||
result = util.exec_command(
|
||||
"echo | %s -dM -E -" % env.subst("$CC"), env=sysenv, shell=True)
|
||||
except OSError:
|
||||
return items
|
||||
if result['returncode'] != 0:
|
||||
return items
|
||||
for line in result['out'].split("\n"):
|
||||
tokens = line.strip().split(" ", 2)
|
||||
if not tokens or tokens[0] != "#define":
|
||||
continue
|
||||
if len(tokens) > 2:
|
||||
items.append("%s=%s" % (tokens[1], tokens[2]))
|
||||
else:
|
||||
items.append(tokens[1])
|
||||
return items
|
||||
|
||||
|
||||
def _dump_defines(env):
|
||||
defines = []
|
||||
# global symbols
|
||||
for item in processDefines(env.get("CPPDEFINES", [])):
|
||||
@ -61,9 +86,18 @@ def dump_defines(env):
|
||||
|
||||
# special symbol for Atmel AVR MCU
|
||||
if env['PIOPLATFORM'] == "atmelavr":
|
||||
defines.append(
|
||||
"__AVR_%s__" % env.BoardConfig().get("build.mcu").upper()
|
||||
.replace("ATMEGA", "ATmega").replace("ATTINY", "ATtiny"))
|
||||
board_mcu = env.get("BOARD_MCU")
|
||||
if not board_mcu and "BOARD" in env:
|
||||
board_mcu = env.BoardConfig().get("build.mcu")
|
||||
if board_mcu:
|
||||
defines.append(
|
||||
str("__AVR_%s__" % board_mcu.upper()
|
||||
.replace("ATMEGA", "ATmega").replace("ATTINY", "ATtiny")))
|
||||
|
||||
# built-in GCC marcos
|
||||
if env.GetCompilerType() == "gcc":
|
||||
defines.extend(_get_gcc_defines(env))
|
||||
|
||||
return defines
|
||||
|
||||
|
||||
@ -75,9 +109,9 @@ def DumpIDEData(env):
|
||||
"libsource_dirs":
|
||||
[env.subst(l) for l in env.get("LIBSOURCE_DIRS", [])],
|
||||
"defines":
|
||||
dump_defines(env),
|
||||
_dump_defines(env),
|
||||
"includes":
|
||||
dump_includes(env),
|
||||
_dump_includes(env),
|
||||
"cc_flags":
|
||||
env.subst(LINTCCOM),
|
||||
"cxx_flags":
|
||||
@ -89,7 +123,9 @@ def DumpIDEData(env):
|
||||
"gdb_path":
|
||||
util.where_is_program(env.subst("$GDB"), env.subst("${ENV['PATH']}")),
|
||||
"prog_path":
|
||||
env.subst("$PROG_PATH")
|
||||
env.subst("$PROG_PATH"),
|
||||
"compiler_type":
|
||||
env.GetCompilerType()
|
||||
}
|
||||
|
||||
env_ = env.Clone()
|
||||
|
@ -17,13 +17,15 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
from os.path import basename, commonprefix, isdir, isfile, join, realpath, sep
|
||||
from os.path import (basename, commonprefix, dirname, isdir, isfile, join,
|
||||
realpath, sep)
|
||||
from platform import system
|
||||
|
||||
import SCons.Scanner
|
||||
from SCons.Script import ARGUMENTS, DefaultEnvironment
|
||||
from SCons.Script import ARGUMENTS, COMMAND_LINE_TARGETS, DefaultEnvironment
|
||||
|
||||
from platformio import util
|
||||
from platformio.builder.tools import platformio as piotool
|
||||
@ -82,9 +84,14 @@ class LibBuilderBase(object):
|
||||
LDF_MODES = ["off", "chain", "deep", "chain+", "deep+"]
|
||||
LDF_MODE_DEFAULT = "chain"
|
||||
|
||||
COMPAT_MODES = [0, 1, 2]
|
||||
COMPAT_MODE_DEFAULT = 1
|
||||
|
||||
CLASSIC_SCANNER = SCons.Scanner.C.CScanner()
|
||||
ADVANCED_SCANNER = SCons.Scanner.C.CScanner(advanced=True)
|
||||
INC_DIRS_CACHE = None
|
||||
PARSE_SRC_BY_H_NAME = True
|
||||
|
||||
_INCLUDE_DIRS_CACHE = None
|
||||
|
||||
def __init__(self, env, path, manifest=None, verbose=False):
|
||||
self.env = env.Clone()
|
||||
@ -93,13 +100,11 @@ class LibBuilderBase(object):
|
||||
self.verbose = verbose
|
||||
|
||||
self._manifest = manifest if manifest else self.load_manifest()
|
||||
self._ldf_mode = self.validate_ldf_mode(
|
||||
self.env.get("LIB_LDF_MODE", self.LDF_MODE_DEFAULT))
|
||||
self._is_dependent = False
|
||||
self._is_built = False
|
||||
self._depbuilders = list()
|
||||
self._circular_deps = list()
|
||||
self._scanned_paths = list()
|
||||
self._processed_files = list()
|
||||
|
||||
# reset source filter, could be overridden with extra script
|
||||
self.env['SRC_FILTER'] = ""
|
||||
@ -140,20 +145,29 @@ class LibBuilderBase(object):
|
||||
"-<tests%s>" % os.sep
|
||||
]
|
||||
|
||||
@property
|
||||
def include_dir(self):
|
||||
if not all([isdir(join(self.path, d)) for d in ("include", "src")]):
|
||||
return None
|
||||
return join(self.path, "include")
|
||||
|
||||
@property
|
||||
def src_dir(self):
|
||||
return (join(self.path, "src")
|
||||
if isdir(join(self.path, "src")) else self.path)
|
||||
|
||||
def get_include_dirs(self):
|
||||
items = [self.src_dir]
|
||||
include_dir = self.include_dir
|
||||
if include_dir and include_dir not in items:
|
||||
items.append(include_dir)
|
||||
return items
|
||||
|
||||
@property
|
||||
def build_dir(self):
|
||||
return join("$BUILD_DIR", "lib", basename(self.path))
|
||||
|
||||
def get_inc_dirs(self):
|
||||
items = [self.src_dir]
|
||||
if all([isdir(join(self.path, d)) for d in ("inc", "src")]):
|
||||
items.append(join(self.path, "inc"))
|
||||
return items
|
||||
return join("$BUILD_DIR",
|
||||
"lib%s" % hashlib.sha1(self.path).hexdigest()[:3],
|
||||
basename(self.path))
|
||||
|
||||
@property
|
||||
def build_flags(self):
|
||||
@ -171,21 +185,15 @@ class LibBuilderBase(object):
|
||||
def lib_archive(self):
|
||||
return self.env.get("LIB_ARCHIVE", "") != "false"
|
||||
|
||||
@staticmethod
|
||||
def validate_ldf_mode(mode):
|
||||
if isinstance(mode, basestring):
|
||||
mode = mode.strip().lower()
|
||||
if mode in LibBuilderBase.LDF_MODES:
|
||||
return mode
|
||||
try:
|
||||
return LibBuilderBase.LDF_MODES[int(mode)]
|
||||
except (IndexError, ValueError):
|
||||
pass
|
||||
return LibBuilderBase.LDF_MODE_DEFAULT
|
||||
|
||||
@property
|
||||
def lib_ldf_mode(self):
|
||||
return self._ldf_mode
|
||||
return self.validate_ldf_mode(
|
||||
self.env.get("LIB_LDF_MODE", self.LDF_MODE_DEFAULT))
|
||||
|
||||
@property
|
||||
def lib_compat_mode(self):
|
||||
return self.validate_compat_mode(
|
||||
self.env.get("LIB_COMPAT_MODE", self.COMPAT_MODE_DEFAULT))
|
||||
|
||||
@property
|
||||
def depbuilders(self):
|
||||
@ -200,15 +208,35 @@ class LibBuilderBase(object):
|
||||
return self._is_built
|
||||
|
||||
@staticmethod
|
||||
def items_in_list(items, ilist):
|
||||
def validate_ldf_mode(mode):
|
||||
if isinstance(mode, basestring):
|
||||
mode = mode.strip().lower()
|
||||
if mode in LibBuilderBase.LDF_MODES:
|
||||
return mode
|
||||
try:
|
||||
return LibBuilderBase.LDF_MODES[int(mode)]
|
||||
except (IndexError, ValueError):
|
||||
pass
|
||||
return LibBuilderBase.LDF_MODE_DEFAULT
|
||||
|
||||
def _items_to_list(items_):
|
||||
if not isinstance(items_, list):
|
||||
items_ = [i.strip() for i in items_.split(",")]
|
||||
return [i.lower() for i in items_ if i]
|
||||
@staticmethod
|
||||
def validate_compat_mode(mode):
|
||||
try:
|
||||
mode = int(mode)
|
||||
assert mode in LibBuilderBase.COMPAT_MODES
|
||||
return mode
|
||||
except (AssertionError, ValueError):
|
||||
return LibBuilderBase.COMPAT_MODE_DEFAULT
|
||||
|
||||
items = _items_to_list(items)
|
||||
ilist = _items_to_list(ilist)
|
||||
@staticmethod
|
||||
def items_to_list(items):
|
||||
if not isinstance(items, list):
|
||||
items = [i.strip() for i in items.split(",")]
|
||||
return [i.lower() for i in items if i]
|
||||
|
||||
def items_in_list(self, items, ilist):
|
||||
items = self.items_to_list(items)
|
||||
ilist = self.items_to_list(ilist)
|
||||
if "*" in items or "*" in ilist:
|
||||
return True
|
||||
return set(items) & set(ilist)
|
||||
@ -222,13 +250,6 @@ class LibBuilderBase(object):
|
||||
def load_manifest(self):
|
||||
return {}
|
||||
|
||||
def get_src_files(self):
|
||||
return [
|
||||
join(self.src_dir, item)
|
||||
for item in self.env.MatchSourceFiles(self.src_dir,
|
||||
self.src_filter)
|
||||
]
|
||||
|
||||
def process_extra_options(self):
|
||||
with util.cd(self.path):
|
||||
self.env.ProcessUnFlags(self.build_unflags)
|
||||
@ -237,10 +258,12 @@ class LibBuilderBase(object):
|
||||
self.env.SConscriptChdir(1)
|
||||
self.env.SConscript(
|
||||
realpath(self.extra_script),
|
||||
exports={"env": self.env,
|
||||
"pio_lib_builder": self})
|
||||
exports={
|
||||
"env": self.env,
|
||||
"pio_lib_builder": self
|
||||
})
|
||||
|
||||
def _process_dependencies(self):
|
||||
def process_dependencies(self):
|
||||
if not self.dependencies:
|
||||
return
|
||||
for item in self.dependencies:
|
||||
@ -260,7 +283,7 @@ class LibBuilderBase(object):
|
||||
continue
|
||||
|
||||
found = False
|
||||
for lb in self.envorigin.GetLibBuilders():
|
||||
for lb in self.env.GetLibBuilders():
|
||||
if item['name'] != lb.name:
|
||||
continue
|
||||
elif "frameworks" in item and \
|
||||
@ -279,56 +302,81 @@ class LibBuilderBase(object):
|
||||
"library\n" % (item['name'], self.name))
|
||||
self.env.Exit(1)
|
||||
|
||||
def _validate_search_paths(self, search_paths=None):
|
||||
if not search_paths:
|
||||
search_paths = []
|
||||
assert isinstance(search_paths, list)
|
||||
def get_search_files(self):
|
||||
items = [
|
||||
join(self.src_dir, item)
|
||||
for item in self.env.MatchSourceFiles(self.src_dir,
|
||||
self.src_filter)
|
||||
]
|
||||
include_dir = self.include_dir
|
||||
if include_dir:
|
||||
items.extend([
|
||||
join(include_dir, item)
|
||||
for item in self.env.MatchSourceFiles(include_dir)
|
||||
])
|
||||
return items
|
||||
|
||||
_search_paths = []
|
||||
for path in search_paths:
|
||||
if path not in self._scanned_paths:
|
||||
_search_paths.append(path)
|
||||
self._scanned_paths.append(path)
|
||||
def _validate_search_files(self, search_files=None):
|
||||
if not search_files:
|
||||
search_files = []
|
||||
assert isinstance(search_files, list)
|
||||
|
||||
return _search_paths
|
||||
_search_files = []
|
||||
for path in search_files:
|
||||
if path not in self._processed_files:
|
||||
_search_files.append(path)
|
||||
self._processed_files.append(path)
|
||||
|
||||
def _get_found_includes(self, search_paths=None):
|
||||
return _search_files
|
||||
|
||||
def _get_found_includes(self, search_files=None):
|
||||
# all include directories
|
||||
if not LibBuilderBase.INC_DIRS_CACHE:
|
||||
inc_dirs = []
|
||||
used_inc_dirs = []
|
||||
for lb in self.envorigin.GetLibBuilders():
|
||||
items = [self.env.Dir(d) for d in lb.get_inc_dirs()]
|
||||
if lb.dependent:
|
||||
used_inc_dirs.extend(items)
|
||||
else:
|
||||
inc_dirs.extend(items)
|
||||
LibBuilderBase.INC_DIRS_CACHE = used_inc_dirs + inc_dirs
|
||||
if not LibBuilderBase._INCLUDE_DIRS_CACHE:
|
||||
LibBuilderBase._INCLUDE_DIRS_CACHE = []
|
||||
for lb in self.env.GetLibBuilders():
|
||||
LibBuilderBase._INCLUDE_DIRS_CACHE.extend(
|
||||
[self.env.Dir(d) for d in lb.get_include_dirs()])
|
||||
|
||||
# append self include directories
|
||||
inc_dirs = [self.env.Dir(d) for d in self.get_inc_dirs()]
|
||||
inc_dirs.extend(LibBuilderBase.INC_DIRS_CACHE)
|
||||
include_dirs = [self.env.Dir(d) for d in self.get_include_dirs()]
|
||||
include_dirs.extend(LibBuilderBase._INCLUDE_DIRS_CACHE)
|
||||
|
||||
result = []
|
||||
for path in self._validate_search_paths(search_paths):
|
||||
for path in self._validate_search_files(search_files):
|
||||
try:
|
||||
assert "+" in self.lib_ldf_mode
|
||||
incs = self.env.File(path).get_found_includes(
|
||||
self.env, LibBuilderBase.ADVANCED_SCANNER, tuple(inc_dirs))
|
||||
self.env, LibBuilderBase.ADVANCED_SCANNER,
|
||||
tuple(include_dirs))
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
if self.verbose and "+" in self.lib_ldf_mode:
|
||||
sys.stderr.write(
|
||||
"Warning! Classic Pre Processor is used for `%s`, "
|
||||
"advanced has failed with `%s`\n" % (path, e))
|
||||
incs = self.env.File(path).get_found_includes(
|
||||
self.env, LibBuilderBase.CLASSIC_SCANNER, tuple(inc_dirs))
|
||||
_incs = self.env.File(path).get_found_includes(
|
||||
self.env, LibBuilderBase.CLASSIC_SCANNER,
|
||||
tuple(include_dirs))
|
||||
incs = []
|
||||
for inc in _incs:
|
||||
incs.append(inc)
|
||||
if not self.PARSE_SRC_BY_H_NAME:
|
||||
continue
|
||||
_h_path = inc.get_abspath()
|
||||
if not self.env.IsFileWithExt(_h_path,
|
||||
piotool.SRC_HEADER_EXT):
|
||||
continue
|
||||
_f_part = _h_path[:_h_path.rindex(".")]
|
||||
for ext in piotool.SRC_C_EXT:
|
||||
if isfile("%s.%s" % (_f_part, ext)):
|
||||
incs.append(
|
||||
self.env.File("%s.%s" % (_f_part, ext)))
|
||||
# print path, map(lambda n: n.get_abspath(), incs)
|
||||
for inc in incs:
|
||||
if inc not in result:
|
||||
result.append(inc)
|
||||
return result
|
||||
|
||||
def depend_recursive(self, lb, search_paths=None):
|
||||
def depend_recursive(self, lb, search_files=None):
|
||||
|
||||
def _already_depends(_lb):
|
||||
if self in _lb.depbuilders:
|
||||
@ -348,32 +396,32 @@ class LibBuilderBase(object):
|
||||
self._circular_deps.append(lb)
|
||||
elif lb not in self._depbuilders:
|
||||
self._depbuilders.append(lb)
|
||||
LibBuilderBase.INC_DIRS_CACHE = None
|
||||
lb.search_deps_recursive(search_paths)
|
||||
LibBuilderBase._INCLUDE_DIRS_CACHE = None
|
||||
lb.search_deps_recursive(search_files)
|
||||
|
||||
def search_deps_recursive(self, search_paths=None):
|
||||
def search_deps_recursive(self, search_files=None):
|
||||
if not self._is_dependent:
|
||||
self._is_dependent = True
|
||||
self._process_dependencies()
|
||||
self.process_dependencies()
|
||||
|
||||
if self.lib_ldf_mode.startswith("deep"):
|
||||
search_paths = self.get_src_files()
|
||||
search_files = self.get_search_files()
|
||||
|
||||
# when LDF is disabled
|
||||
if self.lib_ldf_mode == "off":
|
||||
return
|
||||
|
||||
lib_inc_map = {}
|
||||
for inc in self._get_found_includes(search_paths):
|
||||
for lb in self.envorigin.GetLibBuilders():
|
||||
for inc in self._get_found_includes(search_files):
|
||||
for lb in self.env.GetLibBuilders():
|
||||
if inc.get_abspath() in lb:
|
||||
if lb not in lib_inc_map:
|
||||
lib_inc_map[lb] = []
|
||||
lib_inc_map[lb].append(inc.get_abspath())
|
||||
break
|
||||
|
||||
for lb, lb_search_paths in lib_inc_map.items():
|
||||
self.depend_recursive(lb, lb_search_paths)
|
||||
for lb, lb_search_files in lib_inc_map.items():
|
||||
self.depend_recursive(lb, lb_search_files)
|
||||
|
||||
def build(self):
|
||||
libs = []
|
||||
@ -384,16 +432,16 @@ class LibBuilderBase(object):
|
||||
self.env.AppendUnique(**{key: lb.env.get(key)})
|
||||
|
||||
for lb in self._circular_deps:
|
||||
self.env.AppendUnique(CPPPATH=lb.get_inc_dirs())
|
||||
self.env.AppendUnique(CPPPATH=lb.get_include_dirs())
|
||||
|
||||
if self._is_built:
|
||||
return libs
|
||||
self._is_built = True
|
||||
|
||||
self.env.AppendUnique(CPPPATH=self.get_inc_dirs())
|
||||
self.env.AppendUnique(CPPPATH=self.get_include_dirs())
|
||||
|
||||
if self.lib_ldf_mode == "off":
|
||||
for lb in self.envorigin.GetLibBuilders():
|
||||
for lb in self.env.GetLibBuilders():
|
||||
if self == lb or not lb.is_built:
|
||||
continue
|
||||
for key in ("CPPPATH", "LIBPATH", "LIBS", "LINKFLAGS"):
|
||||
@ -427,13 +475,13 @@ class ArduinoLibBuilder(LibBuilderBase):
|
||||
manifest[key.strip()] = value.strip()
|
||||
return manifest
|
||||
|
||||
def get_inc_dirs(self):
|
||||
inc_dirs = LibBuilderBase.get_inc_dirs(self)
|
||||
def get_include_dirs(self):
|
||||
include_dirs = LibBuilderBase.get_include_dirs(self)
|
||||
if isdir(join(self.path, "src")):
|
||||
return inc_dirs
|
||||
return include_dirs
|
||||
if isdir(join(self.path, "utility")):
|
||||
inc_dirs.append(join(self.path, "utility"))
|
||||
return inc_dirs
|
||||
include_dirs.append(join(self.path, "utility"))
|
||||
return include_dirs
|
||||
|
||||
@property
|
||||
def src_filter(self):
|
||||
@ -458,19 +506,25 @@ class MbedLibBuilder(LibBuilderBase):
|
||||
return {}
|
||||
return util.load_json(join(self.path, "module.json"))
|
||||
|
||||
@property
|
||||
def include_dir(self):
|
||||
if isdir(join(self.path, "include")):
|
||||
return join(self.path, "include")
|
||||
return None
|
||||
|
||||
@property
|
||||
def src_dir(self):
|
||||
if isdir(join(self.path, "source")):
|
||||
return join(self.path, "source")
|
||||
return LibBuilderBase.src_dir.fget(self)
|
||||
|
||||
def get_inc_dirs(self):
|
||||
inc_dirs = LibBuilderBase.get_inc_dirs(self)
|
||||
if self.path not in inc_dirs:
|
||||
inc_dirs.append(self.path)
|
||||
def get_include_dirs(self):
|
||||
include_dirs = LibBuilderBase.get_include_dirs(self)
|
||||
if self.path not in include_dirs:
|
||||
include_dirs.append(self.path)
|
||||
for p in self._manifest.get("extraIncludes", []):
|
||||
inc_dirs.append(join(self.path, p))
|
||||
return inc_dirs
|
||||
include_dirs.append(join(self.path, p))
|
||||
return include_dirs
|
||||
|
||||
def is_frameworks_compatible(self, frameworks):
|
||||
return self.items_in_list(frameworks, ["mbed"])
|
||||
@ -482,6 +536,14 @@ class PlatformIOLibBuilder(LibBuilderBase):
|
||||
assert isfile(join(self.path, "library.json"))
|
||||
manifest = util.load_json(join(self.path, "library.json"))
|
||||
assert "name" in manifest
|
||||
|
||||
# replace "espressif" old name dev/platform with ESP8266
|
||||
if "platforms" in manifest:
|
||||
manifest['platforms'] = [
|
||||
"espressif8266" if p == "espressif" else p
|
||||
for p in self.items_to_list(manifest['platforms'])
|
||||
]
|
||||
|
||||
return manifest
|
||||
|
||||
def _is_arduino_manifest(self):
|
||||
@ -537,6 +599,13 @@ class PlatformIOLibBuilder(LibBuilderBase):
|
||||
self._manifest.get("build").get("libLDFMode"))
|
||||
return LibBuilderBase.lib_ldf_mode.fget(self)
|
||||
|
||||
@property
|
||||
def lib_compat_mode(self):
|
||||
if "libCompatMode" in self._manifest.get("build", {}):
|
||||
return self.validate_compat_mode(
|
||||
self._manifest.get("build").get("libCompatMode"))
|
||||
return LibBuilderBase.lib_compat_mode.fget(self)
|
||||
|
||||
def is_platforms_compatible(self, platforms):
|
||||
items = self._manifest.get("platforms")
|
||||
if not items:
|
||||
@ -549,27 +618,49 @@ class PlatformIOLibBuilder(LibBuilderBase):
|
||||
return LibBuilderBase.is_frameworks_compatible(self, frameworks)
|
||||
return self.items_in_list(frameworks, items)
|
||||
|
||||
def get_inc_dirs(self):
|
||||
inc_dirs = LibBuilderBase.get_inc_dirs(self)
|
||||
def get_include_dirs(self):
|
||||
include_dirs = LibBuilderBase.get_include_dirs(self)
|
||||
|
||||
# backwards compatibility with PlatformIO 2.0
|
||||
# backwards compatibility with PlatformIO 2.0
|
||||
if ("build" not in self._manifest and self._is_arduino_manifest()
|
||||
and not isdir(join(self.path, "src"))
|
||||
and isdir(join(self.path, "utility"))):
|
||||
inc_dirs.append(join(self.path, "utility"))
|
||||
include_dirs.append(join(self.path, "utility"))
|
||||
|
||||
for path in self.env.get("CPPPATH", []):
|
||||
if path not in self.envorigin.get("CPPPATH", []):
|
||||
inc_dirs.append(self.env.subst(path))
|
||||
return inc_dirs
|
||||
include_dirs.append(self.env.subst(path))
|
||||
return include_dirs
|
||||
|
||||
|
||||
class ProjectAsLibBuilder(LibBuilderBase):
|
||||
|
||||
@property
|
||||
def include_dir(self):
|
||||
include_dir = self.env.subst("$PROJECTINCLUDE_DIR")
|
||||
return include_dir if isdir(include_dir) else None
|
||||
|
||||
@property
|
||||
def src_dir(self):
|
||||
return self.env.subst("$PROJECTSRC_DIR")
|
||||
|
||||
def get_include_dirs(self):
|
||||
include_dirs = LibBuilderBase.get_include_dirs(self)
|
||||
include_dirs.append(self.env.subst("$PROJECTINCLUDE_DIR"))
|
||||
return include_dirs
|
||||
|
||||
def get_search_files(self):
|
||||
# project files
|
||||
items = LibBuilderBase.get_search_files(self)
|
||||
# test files
|
||||
if "__test" in COMMAND_LINE_TARGETS:
|
||||
items.extend([
|
||||
join("$PROJECTTEST_DIR", item)
|
||||
for item in self.env.MatchSourceFiles("$PROJECTTEST_DIR",
|
||||
"$PIOTEST_SRC_FILTER")
|
||||
])
|
||||
return items
|
||||
|
||||
@property
|
||||
def lib_ldf_mode(self):
|
||||
mode = LibBuilderBase.lib_ldf_mode.fget(self)
|
||||
@ -586,39 +677,63 @@ class ProjectAsLibBuilder(LibBuilderBase):
|
||||
# skip for project, options are already processed
|
||||
pass
|
||||
|
||||
def search_deps_recursive(self, search_paths=None):
|
||||
for dep in self.env.get("LIB_DEPS", []):
|
||||
for token in ("@", "="):
|
||||
if token in dep:
|
||||
dep, _ = dep.split(token, 1)
|
||||
for lb in self.envorigin.GetLibBuilders():
|
||||
if lb.name == dep:
|
||||
def process_dependencies(self): # pylint: disable=too-many-branches
|
||||
uris = self.env.get("LIB_DEPS", [])
|
||||
if not uris:
|
||||
return
|
||||
storage_dirs = []
|
||||
for lb in self.env.GetLibBuilders():
|
||||
if dirname(lb.path) not in storage_dirs:
|
||||
storage_dirs.append(dirname(lb.path))
|
||||
|
||||
for uri in uris:
|
||||
found = False
|
||||
for storage_dir in storage_dirs:
|
||||
if found:
|
||||
break
|
||||
lm = LibraryManager(storage_dir)
|
||||
pkg_dir = lm.get_package_dir(*lm.parse_pkg_uri(uri))
|
||||
if not pkg_dir:
|
||||
continue
|
||||
for lb in self.env.GetLibBuilders():
|
||||
if lb.path != pkg_dir:
|
||||
continue
|
||||
if lb not in self.depbuilders:
|
||||
self.depend_recursive(lb)
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
for lb in self.env.GetLibBuilders():
|
||||
if lb.name != uri:
|
||||
continue
|
||||
if lb not in self.depbuilders:
|
||||
self.depend_recursive(lb)
|
||||
break
|
||||
return LibBuilderBase.search_deps_recursive(self, search_paths)
|
||||
|
||||
def build(self):
|
||||
self._is_built = True # do not build Project now
|
||||
self.env.AppendUnique(CPPPATH=self.get_inc_dirs())
|
||||
self.env.AppendUnique(CPPPATH=self.get_include_dirs())
|
||||
return LibBuilderBase.build(self)
|
||||
|
||||
|
||||
def GetLibBuilders(env): # pylint: disable=too-many-branches
|
||||
|
||||
if "__PIO_LIB_BUILDERS" in DefaultEnvironment():
|
||||
return DefaultEnvironment()['__PIO_LIB_BUILDERS']
|
||||
return sorted(
|
||||
DefaultEnvironment()['__PIO_LIB_BUILDERS'],
|
||||
key=lambda lb: 0 if lb.dependent else 1)
|
||||
|
||||
items = []
|
||||
compat_mode = int(env.get("LIB_COMPAT_MODE", 1))
|
||||
verbose = (int(ARGUMENTS.get("PIOVERBOSE", 0))
|
||||
and not env.GetOption('clean'))
|
||||
verbose = int(ARGUMENTS.get("PIOVERBOSE",
|
||||
0)) and not env.GetOption('clean')
|
||||
|
||||
def _check_lib_builder(lb):
|
||||
compat_mode = lb.lib_compat_mode
|
||||
if lb.name in env.get("LIB_IGNORE", []):
|
||||
if verbose:
|
||||
sys.stderr.write("Ignored library %s\n" % lb.path)
|
||||
return
|
||||
return None
|
||||
if compat_mode > 1 and not lb.is_platforms_compatible(
|
||||
env['PIOPLATFORM']):
|
||||
if verbose:
|
||||
@ -678,7 +793,7 @@ def BuildProjectLibraries(env):
|
||||
found_lbs = [lb for lb in lib_builders if lb.dependent]
|
||||
for lb in lib_builders:
|
||||
if lb in found_lbs:
|
||||
lb.search_deps_recursive(lb.get_src_files())
|
||||
lb.search_deps_recursive(lb.get_search_files())
|
||||
for lb in lib_builders:
|
||||
for deplb in lb.depbuilders[:]:
|
||||
if deplb not in found_lbs:
|
||||
@ -690,9 +805,12 @@ def BuildProjectLibraries(env):
|
||||
title = "<%s>" % lb.name
|
||||
if lb.version:
|
||||
title += " v%s" % lb.version
|
||||
sys.stdout.write("%s|-- %s" % (margin, title))
|
||||
if int(ARGUMENTS.get("PIOVERBOSE", 0)):
|
||||
title += " (%s)" % lb.path
|
||||
print "%s|-- %s" % (margin, title)
|
||||
sys.stdout.write(" (")
|
||||
sys.stdout.write(lb.path)
|
||||
sys.stdout.write(")")
|
||||
sys.stdout.write("\n")
|
||||
if lb.depbuilders:
|
||||
print_deps_tree(lb, level + 1)
|
||||
|
||||
@ -709,10 +827,10 @@ def BuildProjectLibraries(env):
|
||||
correct_found_libs()
|
||||
|
||||
if project.depbuilders:
|
||||
print "Library Dependency Graph"
|
||||
print "Library Dependency Graph ( http://bit.ly/configure-pio-ldf )"
|
||||
print_deps_tree(project)
|
||||
else:
|
||||
print "Project does not have dependencies"
|
||||
print "No dependencies"
|
||||
|
||||
return project.build()
|
||||
|
||||
|
@ -50,7 +50,7 @@ class InoToCPPConverter(object):
|
||||
def convert(self, nodes):
|
||||
contents = self.merge(nodes)
|
||||
if not contents:
|
||||
return
|
||||
return None
|
||||
return self.process(contents)
|
||||
|
||||
def merge(self, nodes):
|
||||
@ -199,7 +199,8 @@ def _delete_file(path):
|
||||
pass
|
||||
|
||||
|
||||
def GetCompilerType(env):
|
||||
@util.memoized
|
||||
def _get_compiler_type(env):
|
||||
try:
|
||||
sysenv = environ.copy()
|
||||
sysenv['PATH'] = str(env['ENV']['PATH'])
|
||||
@ -216,6 +217,10 @@ def GetCompilerType(env):
|
||||
return None
|
||||
|
||||
|
||||
def GetCompilerType(env):
|
||||
return _get_compiler_type(env)
|
||||
|
||||
|
||||
def GetActualLDScript(env):
|
||||
|
||||
def _lookup_in_ldpath(script):
|
||||
@ -271,7 +276,7 @@ def PioClean(env, clean_dir):
|
||||
|
||||
def ProcessDebug(env):
|
||||
if not env.subst("$PIODEBUGFLAGS"):
|
||||
env.Replace(PIODEBUGFLAGS=["-Og", "-g3", "-ggdb"])
|
||||
env.Replace(PIODEBUGFLAGS=["-Og", "-g3", "-ggdb3"])
|
||||
env.Append(
|
||||
BUILD_FLAGS=env.get("PIODEBUGFLAGS", []),
|
||||
BUILD_UNFLAGS=["-Os", "-O0", "-O1", "-O2", "-O3"])
|
||||
@ -288,11 +293,12 @@ def ProcessTest(env):
|
||||
src_filter = ["+<*.cpp>", "+<*.c>"]
|
||||
if "PIOTEST" in env:
|
||||
src_filter.append("+<%s%s>" % (env['PIOTEST'], sep))
|
||||
env.Replace(PIOTEST_SRC_FILTER=src_filter)
|
||||
|
||||
return env.CollectBuildFiles(
|
||||
"$BUILDTEST_DIR",
|
||||
"$PROJECTTEST_DIR",
|
||||
src_filter=src_filter,
|
||||
"$PIOTEST_SRC_FILTER",
|
||||
duplicate=False)
|
||||
|
||||
|
||||
|
@ -41,8 +41,9 @@ def PioPlatform(env):
|
||||
def BoardConfig(env, board=None):
|
||||
p = initPioPlatform(env['PLATFORM_MANIFEST'])
|
||||
try:
|
||||
config = p.board_config(board if board else env['BOARD'])
|
||||
except exception.UnknownBoard as e:
|
||||
assert env.get("BOARD", board), "BoardConfig: Board is not defined"
|
||||
config = p.board_config(board if board else env.get("BOARD"))
|
||||
except (AssertionError, exception.UnknownBoard) as e:
|
||||
sys.stderr.write("Error: %s\n" % str(e))
|
||||
env.Exit(1)
|
||||
return config
|
||||
@ -61,6 +62,9 @@ def LoadPioPlatform(env, variables):
|
||||
p = env.PioPlatform()
|
||||
installed_packages = p.get_installed_packages()
|
||||
|
||||
# Ensure real platform name
|
||||
env['PIOPLATFORM'] = p.name
|
||||
|
||||
# Add toolchains and uploaders to $PATH
|
||||
for name in installed_packages:
|
||||
type_ = p.get_package_type(name)
|
||||
@ -80,9 +84,8 @@ def LoadPioPlatform(env, variables):
|
||||
|
||||
board_config = env.BoardConfig()
|
||||
for k in variables.keys():
|
||||
if (k in env
|
||||
or not any([k.startswith("BOARD_"),
|
||||
k.startswith("UPLOAD_")])):
|
||||
if k in env or \
|
||||
not any([k.startswith("BOARD_"), k.startswith("UPLOAD_")]):
|
||||
continue
|
||||
_opt, _val = k.lower().split("_", 1)
|
||||
if _opt == "board":
|
||||
|
@ -58,7 +58,7 @@ def WaitForNewSerialPort(env, before):
|
||||
elapsed = 0
|
||||
before = [p['port'] for p in before]
|
||||
while elapsed < 5 and new_port is None:
|
||||
now = [p['port'] for p in util.get_serialports()]
|
||||
now = [p['port'] for p in util.get_serial_ports()]
|
||||
for p in now:
|
||||
if p not in before:
|
||||
new_port = p
|
||||
@ -107,29 +107,33 @@ def AutodetectUploadPort(*args, **kwargs): # pylint: disable=unused-argument
|
||||
|
||||
def _look_for_mbed_disk():
|
||||
msdlabels = ("mbed", "nucleo", "frdm", "microbit")
|
||||
for item in util.get_logicaldisks():
|
||||
if item['disk'].startswith(
|
||||
"/net") or not _is_match_pattern(item['disk']):
|
||||
for item in util.get_logical_devices():
|
||||
if item['path'].startswith("/net") or not _is_match_pattern(
|
||||
item['path']):
|
||||
continue
|
||||
mbed_pages = [
|
||||
join(item['disk'], n) for n in ("mbed.htm", "mbed.html")
|
||||
join(item['path'], n) for n in ("mbed.htm", "mbed.html")
|
||||
]
|
||||
if any([isfile(p) for p in mbed_pages]):
|
||||
return item['disk']
|
||||
if (item['name']
|
||||
and any([l in item['name'].lower() for l in msdlabels])):
|
||||
return item['disk']
|
||||
return item['path']
|
||||
if item['name'] \
|
||||
and any([l in item['name'].lower() for l in msdlabels]):
|
||||
return item['path']
|
||||
return None
|
||||
|
||||
def _look_for_serial_port():
|
||||
port = None
|
||||
board_hwids = []
|
||||
upload_protocol = env.subst("$UPLOAD_PROTOCOL")
|
||||
if "BOARD" in env and "build.hwids" in env.BoardConfig():
|
||||
board_hwids = env.BoardConfig().get("build.hwids")
|
||||
for item in util.get_serialports(filter_hwid=True):
|
||||
for item in util.get_serial_ports(filter_hwid=True):
|
||||
if not _is_match_pattern(item['port']):
|
||||
continue
|
||||
port = item['port']
|
||||
if upload_protocol.startswith("blackmagic") \
|
||||
and "GDB" in item['description']:
|
||||
return port
|
||||
for hwid in board_hwids:
|
||||
hwid_str = ("%s:%s" % (hwid[0], hwid[1])).replace("0x", "")
|
||||
if hwid_str in item['hwid']:
|
||||
@ -140,7 +144,8 @@ def AutodetectUploadPort(*args, **kwargs): # pylint: disable=unused-argument
|
||||
print env.subst("Use manually specified: $UPLOAD_PORT")
|
||||
return
|
||||
|
||||
if "mbed" in env.subst("$PIOFRAMEWORK"):
|
||||
if "mbed" in env.subst("$PIOFRAMEWORK") \
|
||||
and not env.subst("$UPLOAD_PROTOCOL"):
|
||||
env.Replace(UPLOAD_PORT=_look_for_mbed_disk())
|
||||
else:
|
||||
if (system() == "Linux" and not any([
|
||||
|
@ -72,7 +72,7 @@ def exists(_):
|
||||
|
||||
def generate(env):
|
||||
if system() != "Windows":
|
||||
return
|
||||
return None
|
||||
|
||||
env.Replace(_long_sources_hook=long_sources_hook)
|
||||
env.Replace(_long_incflags_hook=long_incflags_hook)
|
||||
|
@ -27,18 +27,18 @@ from SCons.Util import case_sensitive_suffixes, is_Sequence
|
||||
|
||||
from platformio.util import glob_escape, pioversion_to_intstr
|
||||
|
||||
SRC_BUILD_EXT = ["c", "cc", "cpp", "S", "spp", "SPP", "sx", "s", "asm", "ASM"]
|
||||
SRC_HEADER_EXT = ["h", "hpp"]
|
||||
SRC_C_EXT = ["c", "cc", "cpp"]
|
||||
SRC_BUILD_EXT = SRC_C_EXT + ["S", "spp", "SPP", "sx", "s", "asm", "ASM"]
|
||||
SRC_FILTER_DEFAULT = ["+<*>", "-<.git%s>" % sep, "-<svn%s>" % sep]
|
||||
|
||||
|
||||
def BuildProgram(env):
|
||||
|
||||
def _append_pio_macros():
|
||||
env.AppendUnique(CPPDEFINES=[
|
||||
("PLATFORMIO",
|
||||
int("{0:02d}{1:02d}{2:02d}".format(*pioversion_to_intstr())))
|
||||
])
|
||||
env.AppendUnique(CPPDEFINES=[(
|
||||
"PLATFORMIO",
|
||||
int("{0:02d}{1:02d}{2:02d}".format(*pioversion_to_intstr())))])
|
||||
|
||||
_append_pio_macros()
|
||||
|
||||
@ -46,9 +46,6 @@ def BuildProgram(env):
|
||||
if not case_sensitive_suffixes(".s", ".S"):
|
||||
env.Replace(AS="$CC", ASCOM="$ASPPCOM")
|
||||
|
||||
if "__debug" in COMMAND_LINE_TARGETS:
|
||||
env.ProcessDebug()
|
||||
|
||||
# process extra flags from board
|
||||
if "BOARD" in env and "build.extra_flags" in env.BoardConfig():
|
||||
env.ProcessFlags(env.BoardConfig().get("build.extra_flags"))
|
||||
@ -57,13 +54,26 @@ def BuildProgram(env):
|
||||
# apply user flags
|
||||
env.ProcessFlags(env.get("BUILD_FLAGS"))
|
||||
|
||||
# process framework scripts
|
||||
env.BuildFrameworks(env.get("PIOFRAMEWORK"))
|
||||
|
||||
# restore PIO macros if it was deleted by framework
|
||||
_append_pio_macros()
|
||||
|
||||
# Search for project source files
|
||||
env.Append(
|
||||
LIBPATH=["$BUILD_DIR"],
|
||||
PIOBUILDFILES=env.CollectBuildFiles(
|
||||
"$BUILDSRC_DIR", "$PROJECTSRC_DIR", "$SRC_FILTER",
|
||||
duplicate=False))
|
||||
|
||||
if "__debug" in COMMAND_LINE_TARGETS:
|
||||
env.ProcessDebug()
|
||||
if "__test" in COMMAND_LINE_TARGETS:
|
||||
env.Append(PIOBUILDFILES=env.ProcessTest())
|
||||
|
||||
# build dependent libs
|
||||
deplibs = env.BuildProjectLibraries()
|
||||
env.Append(LIBS=env.BuildProjectLibraries())
|
||||
|
||||
# append specified LD_SCRIPT
|
||||
if ("LDSCRIPT_PATH" in env
|
||||
@ -71,26 +81,14 @@ def BuildProgram(env):
|
||||
env.Append(LINKFLAGS=['-Wl,-T"$LDSCRIPT_PATH"'])
|
||||
|
||||
# enable "cyclic reference" for linker
|
||||
if env.get("LIBS", deplibs) and env.GetCompilerType() == "gcc":
|
||||
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"))
|
||||
|
||||
env.Append(
|
||||
LIBS=deplibs,
|
||||
LIBPATH=["$BUILD_DIR"],
|
||||
PIOBUILDFILES=env.CollectBuildFiles(
|
||||
"$BUILDSRC_DIR",
|
||||
"$PROJECTSRC_DIR",
|
||||
src_filter=env.get("SRC_FILTER"),
|
||||
duplicate=False))
|
||||
|
||||
if "__test" in COMMAND_LINE_TARGETS:
|
||||
env.Append(PIOBUILDFILES=env.ProcessTest())
|
||||
|
||||
if not env['PIOBUILDFILES'] and not COMMAND_LINE_TARGETS:
|
||||
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"))
|
||||
@ -185,6 +183,7 @@ def MatchSourceFiles(env, src_dir, src_filter=None):
|
||||
items.add(item.replace(src_dir + sep, ""))
|
||||
|
||||
src_dir = env.subst(src_dir)
|
||||
src_filter = env.subst(src_filter) if src_filter else None
|
||||
src_filter = src_filter or SRC_FILTER_DEFAULT
|
||||
if isinstance(src_filter, (list, tuple)):
|
||||
src_filter = " ".join(src_filter)
|
||||
@ -269,12 +268,12 @@ def BuildLibrary(env, variant_dir, src_dir, src_filter=None):
|
||||
lib = env.Clone()
|
||||
return lib.StaticLibrary(
|
||||
lib.subst(variant_dir),
|
||||
lib.CollectBuildFiles(variant_dir, src_dir, src_filter=src_filter))
|
||||
lib.CollectBuildFiles(variant_dir, src_dir, src_filter))
|
||||
|
||||
|
||||
def BuildSources(env, variant_dir, src_dir, src_filter=None):
|
||||
DefaultEnvironment().Append(PIOBUILDFILES=env.Clone().CollectBuildFiles(
|
||||
variant_dir, src_dir, src_filter=src_filter))
|
||||
variant_dir, src_dir, src_filter))
|
||||
|
||||
|
||||
def exists(_):
|
||||
|
@ -16,7 +16,6 @@ import json
|
||||
|
||||
import click
|
||||
|
||||
from platformio.exception import APIRequestError, InternetIsOffline
|
||||
from platformio.managers.platform import PlatformManager
|
||||
|
||||
|
||||
@ -43,6 +42,7 @@ def cli(query, installed, json_output): # pylint: disable=R0912
|
||||
click.secho(platform, bold=True)
|
||||
click.echo("-" * terminal_width)
|
||||
print_boards(boards)
|
||||
return True
|
||||
|
||||
|
||||
def print_boards(boards):
|
||||
@ -80,27 +80,13 @@ def print_boards(boards):
|
||||
|
||||
|
||||
def _get_boards(installed=False):
|
||||
boards = PlatformManager().get_installed_boards()
|
||||
if not installed:
|
||||
know_boards = ["%s:%s" % (b['platform'], b['id']) for b in boards]
|
||||
try:
|
||||
for board in PlatformManager().get_registered_boards():
|
||||
key = "%s:%s" % (board['platform'], board['id'])
|
||||
if key not in know_boards:
|
||||
boards.append(board)
|
||||
except InternetIsOffline:
|
||||
pass
|
||||
return sorted(boards, key=lambda b: b['name'])
|
||||
pm = PlatformManager()
|
||||
return pm.get_installed_boards() if installed else pm.get_all_boards()
|
||||
|
||||
|
||||
def _print_boards_json(query, installed=False):
|
||||
result = []
|
||||
try:
|
||||
boards = _get_boards(installed)
|
||||
except APIRequestError:
|
||||
if not installed:
|
||||
boards = _get_boards(True)
|
||||
for board in boards:
|
||||
for board in _get_boards(installed):
|
||||
if query:
|
||||
search_data = "%s %s" % (board['id'], json.dumps(board).lower())
|
||||
if query.lower() not in search_data.lower():
|
||||
|
@ -28,19 +28,71 @@ def cli():
|
||||
|
||||
|
||||
@cli.command("list", short_help="List devices")
|
||||
@click.option("--serial", is_flag=True, help="List serial ports, default")
|
||||
@click.option("--logical", is_flag=True, help="List logical devices")
|
||||
@click.option("--mdns", is_flag=True, help="List multicast DNS services")
|
||||
@click.option("--json-output", is_flag=True)
|
||||
def device_list(json_output):
|
||||
def device_list( # pylint: disable=too-many-branches
|
||||
serial, logical, mdns, json_output):
|
||||
if not logical and not mdns:
|
||||
serial = True
|
||||
data = {}
|
||||
if serial:
|
||||
data['serial'] = util.get_serial_ports()
|
||||
if logical:
|
||||
data['logical'] = util.get_logical_devices()
|
||||
if mdns:
|
||||
data['mdns'] = util.get_mdns_services()
|
||||
|
||||
single_key = data.keys()[0] if len(data.keys()) == 1 else None
|
||||
|
||||
if json_output:
|
||||
click.echo(json.dumps(util.get_serialports()))
|
||||
return
|
||||
return click.echo(json.dumps(data[single_key] if single_key else data))
|
||||
|
||||
for item in util.get_serialports():
|
||||
click.secho(item['port'], fg="cyan")
|
||||
click.echo("-" * len(item['port']))
|
||||
click.echo("Hardware ID: %s" % item['hwid'])
|
||||
click.echo("Description: %s" % item['description'])
|
||||
click.echo("")
|
||||
titles = {
|
||||
"serial": "Serial Ports",
|
||||
"logical": "Logical Devices",
|
||||
"mdns": "Multicast DNS Services"
|
||||
}
|
||||
|
||||
for key, value in data.iteritems():
|
||||
if not single_key:
|
||||
click.secho(titles[key], bold=True)
|
||||
click.echo("=" * len(titles[key]))
|
||||
|
||||
if key == "serial":
|
||||
for item in value:
|
||||
click.secho(item['port'], fg="cyan")
|
||||
click.echo("-" * len(item['port']))
|
||||
click.echo("Hardware ID: %s" % item['hwid'])
|
||||
click.echo("Description: %s" % item['description'])
|
||||
click.echo("")
|
||||
|
||||
if key == "logical":
|
||||
for item in value:
|
||||
click.secho(item['path'], fg="cyan")
|
||||
click.echo("-" * len(item['path']))
|
||||
click.echo("Name: %s" % item['name'])
|
||||
click.echo("")
|
||||
|
||||
if key == "mdns":
|
||||
for item in value:
|
||||
click.secho(item['name'], fg="cyan")
|
||||
click.echo("-" * len(item['name']))
|
||||
click.echo("Type: %s" % item['type'])
|
||||
click.echo("IP: %s" % item['ip'])
|
||||
click.echo("Port: %s" % item['port'])
|
||||
if item['properties']:
|
||||
click.echo("Properties: %s" % ("; ".join([
|
||||
"%s=%s" % (k, v)
|
||||
for k, v in item['properties'].iteritems()
|
||||
])))
|
||||
click.echo("")
|
||||
|
||||
if single_key:
|
||||
click.echo("")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@cli.command("monitor", short_help="Monitor device (Serial)")
|
||||
@ -123,7 +175,7 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches
|
||||
pass
|
||||
|
||||
if not kwargs['port']:
|
||||
ports = util.get_serialports(filter_hwid=True)
|
||||
ports = util.get_serial_ports(filter_hwid=True)
|
||||
if len(ports) == 1:
|
||||
kwargs['port'] = ports[0]['port']
|
||||
|
||||
@ -154,7 +206,7 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches
|
||||
def get_project_options(project_dir, environment):
|
||||
config = util.load_project_config(project_dir)
|
||||
if not config.sections():
|
||||
return
|
||||
return None
|
||||
|
||||
known_envs = [s[4:] for s in config.sections() if s.startswith("env:")]
|
||||
if environment:
|
||||
@ -163,7 +215,7 @@ def get_project_options(project_dir, environment):
|
||||
raise exception.UnknownEnvNames(environment, ", ".join(known_envs))
|
||||
|
||||
if not known_envs:
|
||||
return
|
||||
return None
|
||||
|
||||
if config.has_option("platformio", "env_default"):
|
||||
env_default = config.get("platformio",
|
||||
|
42
platformio/commands/home.py
Normal file
42
platformio/commands/home.py
Normal file
@ -0,0 +1,42 @@
|
||||
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||
#
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
|
||||
import click
|
||||
import requests
|
||||
|
||||
from platformio.managers.core import pioplus_call
|
||||
|
||||
|
||||
@click.command("home", short_help="PIO Home")
|
||||
@click.option("--port", type=int, default=8008, help="HTTP port, default=8008")
|
||||
@click.option(
|
||||
"--host",
|
||||
default="127.0.0.1",
|
||||
help="HTTP host, default=127.0.0.1. "
|
||||
"You can open PIO Home for inbound connections with --host=0.0.0.0")
|
||||
@click.option("--no-open", is_flag=True)
|
||||
def cli(*args, **kwargs): # pylint: disable=unused-argument
|
||||
pioplus_call(sys.argv[1:])
|
||||
|
||||
|
||||
def shutdown_servers():
|
||||
port = 8010
|
||||
while port < 9000:
|
||||
try:
|
||||
requests.get("http://127.0.0.1:%d?__shutdown__=1" % port)
|
||||
port += 1
|
||||
except: # pylint: disable=bare-except
|
||||
return
|
@ -231,11 +231,6 @@ def init_ci_conf(project_dir):
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
#
|
||||
@ -251,11 +246,6 @@ def init_ci_conf(project_dir):
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
|
@ -33,8 +33,7 @@ from platformio.util import get_api_result
|
||||
"-g",
|
||||
"--global",
|
||||
is_flag=True,
|
||||
help="Manage global PlatformIO"
|
||||
" library storage `%s`" % join(util.get_home_dir(), "lib"))
|
||||
help="Manage global PlatformIO library storage")
|
||||
@click.option(
|
||||
"-d",
|
||||
"--storage-dir",
|
||||
@ -93,11 +92,17 @@ def cli(ctx, **options):
|
||||
"--interactive",
|
||||
is_flag=True,
|
||||
help="Allow to make a choice for all prompts")
|
||||
@click.option(
|
||||
"-f",
|
||||
"--force",
|
||||
is_flag=True,
|
||||
help="Reinstall/redownload library if exists")
|
||||
@click.pass_obj
|
||||
def lib_install(lm, libraries, silent, interactive):
|
||||
def lib_install(lm, libraries, silent, interactive, force):
|
||||
# @TODO "save" option
|
||||
for library in libraries:
|
||||
lm.install(library, silent=silent, interactive=interactive)
|
||||
lm.install(
|
||||
library, silent=silent, interactive=interactive, force=force)
|
||||
|
||||
|
||||
@cli.command("uninstall", short_help="Uninstall libraries")
|
||||
@ -128,7 +133,7 @@ def lib_update(lm, libraries, only_check, json_output):
|
||||
requirements = None
|
||||
url = None
|
||||
if not pkg_dir:
|
||||
name, requirements, url = lm.parse_pkg_input(library)
|
||||
name, requirements, url = lm.parse_pkg_uri(library)
|
||||
pkg_dir = lm.get_package_dir(name, requirements, url)
|
||||
if not pkg_dir:
|
||||
continue
|
||||
@ -143,6 +148,8 @@ def lib_update(lm, libraries, only_check, json_output):
|
||||
for library in libraries:
|
||||
lm.update(library, only_check=only_check)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def print_lib_item(item):
|
||||
click.secho(item['name'], fg="cyan")
|
||||
@ -204,7 +211,7 @@ def lib_search(query, json_output, page, noninteractive, **filters):
|
||||
result = get_api_result(
|
||||
"/v2/lib/search",
|
||||
dict(query=" ".join(query), page=page),
|
||||
cache_valid="3d")
|
||||
cache_valid="1d")
|
||||
|
||||
if json_output:
|
||||
click.echo(json.dumps(result))
|
||||
@ -234,8 +241,8 @@ def lib_search(query, json_output, page, noninteractive, **filters):
|
||||
for item in result['items']:
|
||||
print_lib_item(item)
|
||||
|
||||
if (int(result['page']) * int(result['perpage']) >=
|
||||
int(result['total'])):
|
||||
if (int(result['page']) * int(result['perpage']) >= int(
|
||||
result['total'])):
|
||||
break
|
||||
|
||||
if noninteractive:
|
||||
@ -252,7 +259,7 @@ def lib_search(query, json_output, page, noninteractive, **filters):
|
||||
"/v2/lib/search",
|
||||
{"query": " ".join(query),
|
||||
"page": int(result['page']) + 1},
|
||||
cache_valid="3d")
|
||||
cache_valid="1d")
|
||||
|
||||
|
||||
@cli.command("list", short_help="List installed libraries")
|
||||
@ -265,11 +272,13 @@ def lib_list(lm, json_output):
|
||||
return click.echo(json.dumps(items))
|
||||
|
||||
if not items:
|
||||
return
|
||||
return None
|
||||
|
||||
for item in sorted(items, key=lambda i: i['name']):
|
||||
print_lib_item(item)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@util.memoized
|
||||
def get_builtin_libs(storage_names=None):
|
||||
@ -308,13 +317,15 @@ def lib_builtin(storage, json_output):
|
||||
for item in sorted(storage_['items'], key=lambda i: i['name']):
|
||||
print_lib_item(item)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@cli.command("show", short_help="Show detailed info about a library")
|
||||
@click.argument("library", metavar="[LIBRARY]")
|
||||
@click.option("--json-output", is_flag=True)
|
||||
def lib_show(library, json_output):
|
||||
lm = LibraryManager()
|
||||
name, requirements, _ = lm.parse_pkg_input(library)
|
||||
name, requirements, _ = lm.parse_pkg_uri(library)
|
||||
lib_id = lm.get_pkg_id_by_name(
|
||||
name, requirements, silent=json_output, interactive=not json_output)
|
||||
lib = get_api_result("/lib/info/%d" % lib_id, cache_valid="1d")
|
||||
@ -381,6 +392,8 @@ def lib_show(library, json_output):
|
||||
for row in rows:
|
||||
click.echo(row)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@cli.command("register", short_help="Register a new library")
|
||||
@click.argument("config_url")
|
||||
@ -438,8 +451,8 @@ def lib_stats(json_output):
|
||||
printitem_tpl.format(
|
||||
name=click.style(name, fg="cyan"),
|
||||
url=click.style(
|
||||
"http://platformio.org/lib/search?query=" + quote(
|
||||
"keyword:%s" % name),
|
||||
"http://platformio.org/lib/search?query=" +
|
||||
quote("keyword:%s" % name),
|
||||
fg="blue")))
|
||||
|
||||
for key in ("updated", "added"):
|
||||
@ -468,3 +481,5 @@ def lib_stats(json_output):
|
||||
for item in result.get(key, []):
|
||||
_print_lib_item(item)
|
||||
click.echo()
|
||||
|
||||
return True
|
||||
|
@ -47,7 +47,7 @@ def _print_platforms(platforms):
|
||||
|
||||
|
||||
def _get_registry_platforms():
|
||||
platforms = util.get_api_result("/platforms", cache_valid="30d")
|
||||
platforms = util.get_api_result("/platforms", cache_valid="7d")
|
||||
pm = PlatformManager()
|
||||
for platform in platforms or []:
|
||||
platform['versions'] = pm.get_all_repo_versions(platform['name'])
|
||||
@ -188,7 +188,7 @@ def platform_search(query, json_output):
|
||||
@click.option("--json-output", is_flag=True)
|
||||
def platform_frameworks(query, json_output):
|
||||
frameworks = []
|
||||
for framework in util.get_api_result("/frameworks", cache_valid="30d"):
|
||||
for framework in util.get_api_result("/frameworks", cache_valid="7d"):
|
||||
if query == "all":
|
||||
query = ""
|
||||
search_data = json.dumps(framework)
|
||||
@ -257,7 +257,7 @@ def platform_show(platform, json_output): # pylint: disable=too-many-branches
|
||||
click.echo("Frameworks: %s" % ", ".join(data['frameworks']))
|
||||
|
||||
if not data['packages']:
|
||||
return
|
||||
return None
|
||||
|
||||
if not isinstance(data['packages'][0], dict):
|
||||
click.echo("Packages: %s" % ", ".join(data['packages']))
|
||||
@ -287,21 +287,29 @@ def platform_show(platform, json_output): # pylint: disable=too-many-branches
|
||||
click.echo("------")
|
||||
print_boards(data['boards'])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@cli.command("install", short_help="Install new development platform")
|
||||
@click.argument("platforms", nargs=-1, required=True, metavar="[PLATFORM...]")
|
||||
@click.option("--with-package", multiple=True)
|
||||
@click.option("--without-package", multiple=True)
|
||||
@click.option("--skip-default-package", is_flag=True)
|
||||
@click.option(
|
||||
"-f",
|
||||
"--force",
|
||||
is_flag=True,
|
||||
help="Reinstall/redownload dev/platform and its packages if exist")
|
||||
def platform_install(platforms, with_package, without_package,
|
||||
skip_default_package):
|
||||
skip_default_package, force):
|
||||
pm = PlatformManager()
|
||||
for platform in platforms:
|
||||
if pm.install(
|
||||
name=platform,
|
||||
with_packages=with_package,
|
||||
without_packages=without_package,
|
||||
skip_default_package=skip_default_package):
|
||||
skip_default_package=skip_default_package,
|
||||
force=force):
|
||||
click.secho(
|
||||
"The platform '%s' has been successfully installed!\n"
|
||||
"The rest of packages will be installed automatically "
|
||||
@ -351,7 +359,7 @@ def platform_update(platforms, only_packages, only_check, json_output):
|
||||
requirements = None
|
||||
url = None
|
||||
if not pkg_dir:
|
||||
name, requirements, url = pm.parse_pkg_input(platform)
|
||||
name, requirements, url = pm.parse_pkg_uri(platform)
|
||||
pkg_dir = pm.get_package_dir(name, requirements, url)
|
||||
if not pkg_dir:
|
||||
continue
|
||||
@ -375,3 +383,5 @@ def platform_update(platforms, only_packages, only_check, json_output):
|
||||
pm.update(
|
||||
platform, only_packages=only_packages, only_check=only_check)
|
||||
click.echo()
|
||||
|
||||
return True
|
||||
|
@ -132,8 +132,8 @@ class EnvironmentProcessor(object):
|
||||
"upload_protocol", "upload_speed", "upload_flags",
|
||||
"upload_resetmethod", "lib_deps", "lib_ignore",
|
||||
"lib_extra_dirs", "lib_ldf_mode", "lib_compat_mode",
|
||||
"lib_archive", "piotest", "test_transport", "test_ignore",
|
||||
"test_port", "debug_tool", "debug_port",
|
||||
"lib_archive", "piotest", "test_transport", "test_filter",
|
||||
"test_ignore", "test_port", "debug_tool", "debug_port",
|
||||
"debug_init_cmds", "debug_extra_cmds", "debug_server",
|
||||
"debug_init_break", "debug_load_cmd", "monitor_port",
|
||||
"monitor_baud", "monitor_rts", "monitor_dtr")
|
||||
@ -180,13 +180,14 @@ class EnvironmentProcessor(object):
|
||||
self.options[k] = self.options[k].strip()
|
||||
|
||||
if not self.silent:
|
||||
click.echo(
|
||||
"[%s] Processing %s (%s)" %
|
||||
(datetime.now().strftime("%c"),
|
||||
click.style(self.name, fg="cyan", bold=True), "; ".join([
|
||||
"%s: %s" % (k, ", ".join(util.parse_conf_multi_values(v)))
|
||||
for k, v in self.options.items()
|
||||
])))
|
||||
click.echo("[%s] Processing %s (%s)" %
|
||||
(datetime.now().strftime("%c"),
|
||||
click.style(self.name, fg="cyan", bold=True),
|
||||
"; ".join([
|
||||
"%s: %s" %
|
||||
(k, ", ".join(util.parse_conf_multi_values(v)))
|
||||
for k, v in self.options.items()
|
||||
])))
|
||||
click.secho("-" * terminal_width, bold=True)
|
||||
|
||||
self.options = self._validate_options(self.options)
|
||||
@ -227,7 +228,7 @@ class EnvironmentProcessor(object):
|
||||
v = self.RENAMED_PLATFORMS[v]
|
||||
|
||||
# warn about unknown options
|
||||
if k not in self.KNOWN_OPTIONS:
|
||||
if k not in self.KNOWN_OPTIONS and not k.startswith("custom_"):
|
||||
click.secho(
|
||||
"Detected non-PlatformIO `%s` option in `[env:%s]` section"
|
||||
% (k, self.name),
|
||||
@ -278,10 +279,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'])
|
||||
@ -323,8 +324,8 @@ def _clean_pioenvs_dir(pioenvs_dir):
|
||||
|
||||
# if project's config is modified
|
||||
if (isdir(pioenvs_dir)
|
||||
and getmtime(join(util.get_project_dir(), "platformio.ini")) >
|
||||
getmtime(pioenvs_dir)):
|
||||
and getmtime(join(util.get_project_dir(),
|
||||
"platformio.ini")) > getmtime(pioenvs_dir)):
|
||||
util.rmtree_(pioenvs_dir)
|
||||
|
||||
# check project structure
|
||||
|
@ -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)
|
||||
|
||||
|
@ -18,62 +18,61 @@ import click
|
||||
import requests
|
||||
|
||||
from platformio import VERSION, __version__, exception, util
|
||||
from platformio.managers.core import update_core_packages
|
||||
from platformio.commands.home import shutdown_servers
|
||||
|
||||
|
||||
@click.command(
|
||||
"upgrade", short_help="Upgrade PlatformIO to the latest version")
|
||||
def cli():
|
||||
# Update PlatformIO's Core packages
|
||||
update_core_packages(silent=True)
|
||||
|
||||
latest = get_latest_version()
|
||||
if __version__ == latest:
|
||||
@click.option("--dev", is_flag=True, help="Use development branch")
|
||||
def cli(dev):
|
||||
if not dev and __version__ == get_latest_version():
|
||||
return click.secho(
|
||||
"You're up-to-date!\nPlatformIO %s is currently the "
|
||||
"newest version available." % __version__,
|
||||
fg="green")
|
||||
else:
|
||||
click.secho("Please wait while upgrading PlatformIO ...", fg="yellow")
|
||||
|
||||
to_develop = not all([c.isdigit() for c in latest if c != "."])
|
||||
cmds = ([
|
||||
"pip", "install", "--upgrade",
|
||||
"https://github.com/platformio/platformio-core/archive/develop.zip"
|
||||
if to_develop else "platformio"
|
||||
], ["platformio", "--version"])
|
||||
click.secho("Please wait while upgrading PlatformIO ...", fg="yellow")
|
||||
|
||||
cmd = None
|
||||
r = None
|
||||
try:
|
||||
for cmd in cmds:
|
||||
cmd = [util.get_pythonexe_path(), "-m"] + cmd
|
||||
r = None
|
||||
# kill all PIO Home servers, they block `pioplus` binary
|
||||
shutdown_servers()
|
||||
|
||||
to_develop = dev or not all([c.isdigit() for c in __version__ if c != "."])
|
||||
cmds = ([
|
||||
"pip", "install", "--upgrade",
|
||||
"https://github.com/platformio/platformio-core/archive/develop.zip"
|
||||
if to_develop else "platformio"
|
||||
], ["platformio", "--version"])
|
||||
|
||||
cmd = None
|
||||
r = None
|
||||
try:
|
||||
for cmd in cmds:
|
||||
cmd = [util.get_pythonexe_path(), "-m"] + cmd
|
||||
r = None
|
||||
r = util.exec_command(cmd)
|
||||
|
||||
# try pip with disabled cache
|
||||
if r['returncode'] != 0 and cmd[2] == "pip":
|
||||
cmd.insert(3, "--no-cache-dir")
|
||||
r = util.exec_command(cmd)
|
||||
|
||||
# try pip with disabled cache
|
||||
if r['returncode'] != 0 and cmd[2] == "pip":
|
||||
cmd.insert(3, "--no-cache-dir")
|
||||
r = util.exec_command(cmd)
|
||||
|
||||
assert r['returncode'] == 0
|
||||
assert "version" in r['out']
|
||||
actual_version = r['out'].strip().split("version", 1)[1].strip()
|
||||
assert r['returncode'] == 0
|
||||
assert "version" in r['out']
|
||||
actual_version = r['out'].strip().split("version", 1)[1].strip()
|
||||
click.secho(
|
||||
"PlatformIO has been successfully upgraded to %s" % actual_version,
|
||||
fg="green")
|
||||
click.echo("Release notes: ", nl=False)
|
||||
click.secho(
|
||||
"http://docs.platformio.org/en/latest/history.html", fg="cyan")
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
if not r:
|
||||
raise exception.UpgradeError("\n".join([str(cmd), str(e)]))
|
||||
permission_errors = ("permission denied", "not permitted")
|
||||
if (any([m in r['err'].lower() for m in permission_errors])
|
||||
and "windows" not in util.get_systype()):
|
||||
click.secho(
|
||||
"PlatformIO has been successfully upgraded to %s" %
|
||||
actual_version,
|
||||
fg="green")
|
||||
click.echo("Release notes: ", nl=False)
|
||||
click.secho(
|
||||
"http://docs.platformio.org/en/latest/history.html", fg="cyan")
|
||||
except Exception as e: # pylint: disable=W0703
|
||||
if not r:
|
||||
raise exception.UpgradeError("\n".join([str(cmd), str(e)]))
|
||||
permission_errors = ("permission denied", "not permitted")
|
||||
if (any([m in r['err'].lower() for m in permission_errors])
|
||||
and "windows" not in util.get_systype()):
|
||||
click.secho(
|
||||
"""
|
||||
"""
|
||||
-----------------
|
||||
Permission denied
|
||||
-----------------
|
||||
@ -83,12 +82,14 @@ You need the `sudo` permission to install Python packages. Try
|
||||
|
||||
WARNING! Don't use `sudo` for the rest PlatformIO commands.
|
||||
""",
|
||||
fg="yellow",
|
||||
err=True)
|
||||
raise exception.ReturnErrorCode(1)
|
||||
else:
|
||||
raise exception.UpgradeError(
|
||||
"\n".join([str(cmd), r['out'], r['err']]))
|
||||
fg="yellow",
|
||||
err=True)
|
||||
raise exception.ReturnErrorCode(1)
|
||||
else:
|
||||
raise exception.UpgradeError("\n".join(
|
||||
[str(cmd), r['out'], r['err']]))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_latest_version():
|
||||
|
@ -15,6 +15,7 @@
|
||||
from email.utils import parsedate_tz
|
||||
from math import ceil
|
||||
from os.path import getsize, join
|
||||
from sys import getfilesystemencoding, version_info
|
||||
from time import mktime
|
||||
|
||||
import click
|
||||
@ -30,9 +31,13 @@ class FileDownloader(object):
|
||||
CHUNK_SIZE = 1024
|
||||
|
||||
def __init__(self, url, dest_dir=None):
|
||||
self._request = None
|
||||
# make connection
|
||||
self._request = requests.get(
|
||||
url, stream=True, headers=util.get_request_defheaders())
|
||||
url,
|
||||
stream=True,
|
||||
headers=util.get_request_defheaders(),
|
||||
verify=version_info >= (2, 7, 9))
|
||||
if self._request.status_code != 200:
|
||||
raise FDUnrecognizedStatusCode(self._request.status_code, url)
|
||||
|
||||
@ -48,7 +53,8 @@ class FileDownloader(object):
|
||||
self._progressbar = None
|
||||
self._destination = self._fname
|
||||
if dest_dir:
|
||||
self.set_destination(join(dest_dir, self._fname))
|
||||
self.set_destination(
|
||||
join(dest_dir.decode(getfilesystemencoding()), self._fname))
|
||||
|
||||
def set_destination(self, destination):
|
||||
self._destination = destination
|
||||
@ -65,21 +71,29 @@ class FileDownloader(object):
|
||||
return int(self._request.headers['content-length'])
|
||||
|
||||
def start(self):
|
||||
label = "Downloading"
|
||||
itercontent = self._request.iter_content(chunk_size=self.CHUNK_SIZE)
|
||||
f = open(self._destination, "wb")
|
||||
|
||||
if app.is_disabled_progressbar() or self.get_size() == -1:
|
||||
click.echo("Downloading...")
|
||||
for chunk in itercontent:
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
else:
|
||||
chunks = int(ceil(self.get_size() / float(self.CHUNK_SIZE)))
|
||||
with click.progressbar(length=chunks, label="Downloading") as pb:
|
||||
for _ in pb:
|
||||
f.write(next(itercontent))
|
||||
f.close()
|
||||
self._request.close()
|
||||
try:
|
||||
if app.is_disabled_progressbar() or self.get_size() == -1:
|
||||
click.echo("%s..." % label)
|
||||
for chunk in itercontent:
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
else:
|
||||
chunks = int(ceil(self.get_size() / float(self.CHUNK_SIZE)))
|
||||
with click.progressbar(length=chunks, label=label) as pb:
|
||||
for _ in pb:
|
||||
f.write(next(itercontent))
|
||||
except IOError as e:
|
||||
click.secho(
|
||||
"Error: Please read http://bit.ly/package-manager-ioerror",
|
||||
fg="red",
|
||||
err=True)
|
||||
raise e
|
||||
finally:
|
||||
f.close()
|
||||
self._request.close()
|
||||
|
||||
if self.get_lmtime():
|
||||
self._preserve_filemtime(self.get_lmtime())
|
||||
|
@ -53,15 +53,15 @@ class IncompatiblePlatform(PlatformioException):
|
||||
|
||||
class PlatformNotInstalledYet(PlatformioException):
|
||||
|
||||
MESSAGE = "The platform '{0}' has not been installed yet. "\
|
||||
"Use `platformio platform install {0}` command"
|
||||
MESSAGE = ("The platform '{0}' has not been installed yet. "
|
||||
"Use `platformio platform install {0}` command")
|
||||
|
||||
|
||||
class BoardNotDefined(PlatformioException):
|
||||
|
||||
MESSAGE = "You need to specify board ID using `-b` or `--board` "\
|
||||
"option. Supported boards list is available via "\
|
||||
"`platformio boards` command"
|
||||
MESSAGE = (
|
||||
"You need to specify board ID using `-b` or `--board` option. "
|
||||
"Supported boards list is available via `platformio boards` command")
|
||||
|
||||
|
||||
class UnknownBoard(PlatformioException):
|
||||
@ -91,16 +91,16 @@ class MissingPackageManifest(PlatformioException):
|
||||
|
||||
class UndefinedPackageVersion(PlatformioException):
|
||||
|
||||
MESSAGE = "Could not find a version that satisfies the requirement '{0}'"\
|
||||
" for your system '{1}'"
|
||||
MESSAGE = ("Could not find a version that satisfies the requirement '{0}'"
|
||||
" for your system '{1}'")
|
||||
|
||||
|
||||
class PackageInstallError(PlatformioException):
|
||||
|
||||
MESSAGE = "Could not install '{0}' with version requirements '{1}' "\
|
||||
"for your system '{2}'.\n"\
|
||||
"If you use Antivirus, it can block PlatformIO Package "\
|
||||
"Manager. Try to disable it for a while."
|
||||
MESSAGE = (
|
||||
"Could not install '{0}' with version requirements '{1}' for your "
|
||||
"system '{2}'.\n If you use Antivirus, it can block PlatformIO "
|
||||
"Package Manager. Try to disable it for a while.")
|
||||
|
||||
|
||||
class FDUnrecognizedStatusCode(PlatformioException):
|
||||
@ -110,21 +110,22 @@ class FDUnrecognizedStatusCode(PlatformioException):
|
||||
|
||||
class FDSizeMismatch(PlatformioException):
|
||||
|
||||
MESSAGE = "The size ({0:d} bytes) of downloaded file '{1}' "\
|
||||
"is not equal to remote size ({2:d} bytes)"
|
||||
MESSAGE = ("The size ({0:d} bytes) of downloaded file '{1}' "
|
||||
"is not equal to remote size ({2:d} bytes)")
|
||||
|
||||
|
||||
class FDSHASumMismatch(PlatformioException):
|
||||
|
||||
MESSAGE = "The 'sha1' sum '{0}' of downloaded file '{1}' "\
|
||||
"is not equal to remote '{2}'"
|
||||
MESSAGE = ("The 'sha1' sum '{0}' of downloaded file '{1}' "
|
||||
"is not equal to remote '{2}'")
|
||||
|
||||
|
||||
class NotPlatformIOProject(PlatformioException):
|
||||
|
||||
MESSAGE = "Not a PlatformIO project. `platformio.ini` file has not been "\
|
||||
"found in current working directory ({0}). To initialize new project "\
|
||||
"please use `platformio init` command"
|
||||
MESSAGE = (
|
||||
"Not a PlatformIO project. `platformio.ini` file has not been "
|
||||
"found in current working directory ({0}). To initialize new project "
|
||||
"please use `platformio init` command")
|
||||
|
||||
|
||||
class UndefinedEnvPlatform(PlatformioException):
|
||||
@ -164,24 +165,27 @@ class APIRequestError(PlatformioException):
|
||||
|
||||
class InternetIsOffline(PlatformioException):
|
||||
|
||||
MESSAGE = "You are not connected to the Internet"
|
||||
MESSAGE = (
|
||||
"You are not connected to the Internet.\n"
|
||||
"If you build a project first time, we need Internet connection "
|
||||
"to install all dependencies and toolchain.")
|
||||
|
||||
|
||||
class LibNotFound(PlatformioException):
|
||||
|
||||
MESSAGE = "Library `{0}` has not been found in PlatformIO Registry.\n"\
|
||||
"You can ignore this message, if `{0}` is a built-in library "\
|
||||
"(included in framework, SDK). E.g., SPI, Wire, etc."
|
||||
MESSAGE = ("Library `{0}` has not been found in PlatformIO Registry.\n"
|
||||
"You can ignore this message, if `{0}` is a built-in library "
|
||||
"(included in framework, SDK). E.g., SPI, Wire, etc.")
|
||||
|
||||
|
||||
class NotGlobalLibDir(PlatformioException):
|
||||
|
||||
MESSAGE = "The `{0}` is not a PlatformIO project.\n\n"\
|
||||
"To manage libraries "\
|
||||
"in global storage `{1}`,\n"\
|
||||
"please use `platformio lib --global {2}` or specify custom "\
|
||||
"storage `platformio lib --storage-dir /path/to/storage/ {2}`."\
|
||||
"\nCheck `platformio lib --help` for details."
|
||||
MESSAGE = (
|
||||
"The `{0}` is not a PlatformIO project.\n\n"
|
||||
"To manage libraries in global storage `{1}`,\n"
|
||||
"please use `platformio lib --global {2}` or specify custom storage "
|
||||
"`platformio lib --storage-dir /path/to/storage/ {2}`.\n"
|
||||
"Check `platformio lib --help` for details.")
|
||||
|
||||
|
||||
class InvalidLibConfURL(PlatformioException):
|
||||
@ -206,9 +210,9 @@ class InvalidSettingValue(PlatformioException):
|
||||
|
||||
class CIBuildEnvsEmpty(PlatformioException):
|
||||
|
||||
MESSAGE = "Can't find PlatformIO build environments.\n"\
|
||||
"Please specify `--board` or path to `platformio.ini` with "\
|
||||
"predefined environments using `--project-conf` option"
|
||||
MESSAGE = ("Can't find PlatformIO build environments.\n"
|
||||
"Please specify `--board` or path to `platformio.ini` with "
|
||||
"predefined environments using `--project-conf` option")
|
||||
|
||||
|
||||
class UpgradeError(PlatformioException):
|
||||
@ -221,7 +225,17 @@ class UpgradeError(PlatformioException):
|
||||
"""
|
||||
|
||||
|
||||
class HomeDirPermissionsError(PlatformioException):
|
||||
|
||||
MESSAGE = (
|
||||
"The directory `{0}` or its parent directory is not owned by the "
|
||||
"current user and PlatformIO can not store configuration data.\n"
|
||||
"Please check the permissions and owner of that directory.\n"
|
||||
"Otherwise, please remove manually `{0}` directory and PlatformIO "
|
||||
"will create new from the current user.")
|
||||
|
||||
|
||||
class CygwinEnvDetected(PlatformioException):
|
||||
|
||||
MESSAGE = "PlatformIO does not work within Cygwin environment. "\
|
||||
"Use native Terminal instead."
|
||||
MESSAGE = ("PlatformIO does not work within Cygwin environment. "
|
||||
"Use native Terminal instead.")
|
||||
|
@ -15,11 +15,10 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from cStringIO import StringIO
|
||||
from os.path import abspath, basename, expanduser, isdir, isfile, join, relpath
|
||||
|
||||
import bottle
|
||||
import click
|
||||
from click.testing import CliRunner
|
||||
|
||||
from platformio import exception, util
|
||||
from platformio.commands.run import cli as cmd_run
|
||||
@ -60,24 +59,28 @@ class ProjectGenerator(object):
|
||||
|
||||
@util.memoized
|
||||
def get_project_build_data(self):
|
||||
data = {"defines": [], "includes": [], "cxx_path": None}
|
||||
data = {
|
||||
"defines": [],
|
||||
"includes": [],
|
||||
"cxx_path": None,
|
||||
"prog_path": None
|
||||
}
|
||||
envdata = self.get_project_env()
|
||||
if not envdata:
|
||||
return data
|
||||
|
||||
out = StringIO()
|
||||
with util.capture_stdout(out):
|
||||
click.get_current_context().invoke(
|
||||
cmd_run,
|
||||
project_dir=self.project_dir,
|
||||
environment=[envdata['env_name']],
|
||||
target=["idedata"])
|
||||
result = out.getvalue()
|
||||
result = CliRunner().invoke(cmd_run, [
|
||||
"--project-dir", self.project_dir, "--environment",
|
||||
envdata['env_name'], "--target", "idedata"
|
||||
])
|
||||
|
||||
if '"includes":' not in result:
|
||||
raise exception.PlatformioException(result)
|
||||
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)
|
||||
|
||||
for line in result.split("\n"):
|
||||
for line in result.output.split("\n"):
|
||||
line = line.strip()
|
||||
if line.startswith('{"') and line.endswith("}"):
|
||||
data = json.loads(line)
|
||||
|
@ -1,3 +1,3 @@
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode
|
||||
.vscode/c_cpp_properties.json
|
||||
|
@ -31,7 +31,8 @@
|
||||
"{{!define.replace('"', '\\"')}}",
|
||||
% end
|
||||
""
|
||||
]
|
||||
],
|
||||
"intelliSenseMode": "clang-x64"
|
||||
}
|
||||
]
|
||||
}
|
@ -34,33 +34,10 @@ from platformio.managers.lib import LibraryManager
|
||||
from platformio.managers.platform import PlatformFactory, PlatformManager
|
||||
|
||||
|
||||
def in_silence(ctx=None):
|
||||
ctx = ctx or app.get_session_var("command_ctx")
|
||||
assert ctx
|
||||
ctx_args = ctx.args or []
|
||||
return ctx_args and any([
|
||||
ctx.args[0] == "upgrade", "--json-output" in ctx_args,
|
||||
"--version" in ctx_args
|
||||
])
|
||||
|
||||
|
||||
def on_platformio_start(ctx, force, caller):
|
||||
if not caller:
|
||||
if getenv("PLATFORMIO_CALLER"):
|
||||
caller = getenv("PLATFORMIO_CALLER")
|
||||
elif getenv("VSCODE_PID") or getenv("VSCODE_NLS_CONFIG"):
|
||||
caller = "vscode"
|
||||
elif util.is_container():
|
||||
if getenv("C9_UID"):
|
||||
caller = "C9"
|
||||
elif getenv("USER") == "cabox":
|
||||
caller = "CA"
|
||||
elif getenv("CHE_API", getenv("CHE_API_ENDPOINT")):
|
||||
caller = "Che"
|
||||
|
||||
app.set_session_var("command_ctx", ctx)
|
||||
app.set_session_var("force_option", force)
|
||||
app.set_session_var("caller_id", caller)
|
||||
set_caller(caller)
|
||||
telemetry.on_command()
|
||||
|
||||
if not in_silence(ctx):
|
||||
@ -75,7 +52,8 @@ def on_platformio_end(ctx, result): # pylint: disable=W0613
|
||||
check_platformio_upgrade()
|
||||
check_internal_updates(ctx, "platforms")
|
||||
check_internal_updates(ctx, "libraries")
|
||||
except (exception.GetLatestVersionError, exception.APIRequestError):
|
||||
except (exception.InternetIsOffline, exception.GetLatestVersionError,
|
||||
exception.APIRequestError):
|
||||
click.secho(
|
||||
"Failed to check for PlatformIO upgrades. "
|
||||
"Please check your Internet connection.",
|
||||
@ -86,6 +64,32 @@ def on_platformio_exception(e):
|
||||
telemetry.on_exception(e)
|
||||
|
||||
|
||||
def in_silence(ctx=None):
|
||||
ctx = ctx or app.get_session_var("command_ctx")
|
||||
assert ctx
|
||||
ctx_args = ctx.args or []
|
||||
return ctx_args and any([
|
||||
ctx.args[0] == "upgrade", "--json-output" in ctx_args,
|
||||
"--version" in ctx_args
|
||||
])
|
||||
|
||||
|
||||
def set_caller(caller=None):
|
||||
if not caller:
|
||||
if getenv("PLATFORMIO_CALLER"):
|
||||
caller = getenv("PLATFORMIO_CALLER")
|
||||
elif getenv("VSCODE_PID") or getenv("VSCODE_NLS_CONFIG"):
|
||||
caller = "vscode"
|
||||
elif util.is_container():
|
||||
if getenv("C9_UID"):
|
||||
caller = "C9"
|
||||
elif getenv("USER") == "cabox":
|
||||
caller = "CA"
|
||||
elif getenv("CHE_API", getenv("CHE_API_ENDPOINT")):
|
||||
caller = "Che"
|
||||
app.set_session_var("caller_id", caller)
|
||||
|
||||
|
||||
class Upgrader(object):
|
||||
|
||||
def __init__(self, from_version, to_version):
|
||||
@ -98,7 +102,7 @@ class Upgrader(object):
|
||||
self._upgrade_to_3_0_0),
|
||||
(semantic_version.Version("3.0.0-b.11"),
|
||||
self._upgrade_to_3_0_0b11),
|
||||
(semantic_version.Version("3.4.0-a.9"),
|
||||
(semantic_version.Version("3.5.0-a.2"),
|
||||
self._update_dev_platforms)]
|
||||
|
||||
def run(self, ctx):
|
||||
@ -234,12 +238,14 @@ def check_platformio_upgrade():
|
||||
if (time() - interval) < last_check.get("platformio_upgrade", 0):
|
||||
return
|
||||
|
||||
# Update PlatformIO's Core packages
|
||||
update_core_packages(silent=True)
|
||||
|
||||
last_check['platformio_upgrade'] = int(time())
|
||||
app.set_state_item("last_check", last_check)
|
||||
|
||||
util.internet_on(raise_exception=True)
|
||||
|
||||
# Update PlatformIO's Core packages
|
||||
update_core_packages(silent=True)
|
||||
|
||||
latest_version = get_latest_version()
|
||||
if semantic_version.Version.coerce(util.pepver_to_semver(
|
||||
latest_version)) <= semantic_version.Version.coerce(
|
||||
@ -282,6 +288,8 @@ def check_internal_updates(ctx, what):
|
||||
last_check[what + '_update'] = int(time())
|
||||
app.set_state_item("last_check", last_check)
|
||||
|
||||
util.internet_on(raise_exception=True)
|
||||
|
||||
pm = PlatformManager() if what == "platforms" else LibraryManager()
|
||||
outdated_items = []
|
||||
for manifest in pm.get_installed():
|
||||
|
@ -15,14 +15,15 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from os.path import join
|
||||
from os.path import dirname, join
|
||||
|
||||
from platformio import __version__, exception, util
|
||||
from platformio.managers.package import PackageManager
|
||||
|
||||
CORE_PACKAGES = {
|
||||
"pysite-pioplus": ">=0.3.0,<2",
|
||||
"tool-pioplus": ">=0.9.1,<2",
|
||||
"contrib-piohome": ">=0.6.0,<2",
|
||||
"contrib-pysite": ">=0.1.2,<2",
|
||||
"tool-pioplus": ">=0.12.1,<2",
|
||||
"tool-unity": "~1.20302.1",
|
||||
"tool-scons": "~3.20501.2"
|
||||
}
|
||||
@ -35,15 +36,18 @@ PIOPLUS_AUTO_UPDATES_MAX = 100
|
||||
class CorePackageManager(PackageManager):
|
||||
|
||||
def __init__(self):
|
||||
PackageManager.__init__(
|
||||
self,
|
||||
join(util.get_home_dir(), "packages"), [
|
||||
"https://dl.bintray.com/platformio/dl-packages/manifest.json",
|
||||
"http%s://dl.platformio.org/packages/manifest.json" %
|
||||
("" if sys.version_info < (2, 7, 9) else "s")
|
||||
])
|
||||
PackageManager.__init__(self, join(util.get_home_dir(), "packages"), [
|
||||
"https://dl.bintray.com/platformio/dl-packages/manifest.json",
|
||||
"http%s://dl.platformio.org/packages/manifest.json" %
|
||||
("" if sys.version_info < (2, 7, 9) else "s")
|
||||
])
|
||||
|
||||
def install(self, name, requirements=None, *args, **kwargs):
|
||||
def install( # pylint: disable=keyword-arg-before-vararg
|
||||
self,
|
||||
name,
|
||||
requirements=None,
|
||||
*args,
|
||||
**kwargs):
|
||||
PackageManager.install(self, name, requirements, *args, **kwargs)
|
||||
self.cleanup_packages()
|
||||
return self.get_package_dir(name, requirements)
|
||||
@ -71,7 +75,8 @@ class CorePackageManager(PackageManager):
|
||||
|
||||
|
||||
def get_core_package_dir(name):
|
||||
assert name in CORE_PACKAGES
|
||||
if name not in CORE_PACKAGES:
|
||||
raise exception.PlatformioException("Please upgrade PIO Core")
|
||||
requirements = CORE_PACKAGES[name]
|
||||
pm = CorePackageManager()
|
||||
pkg_dir = pm.get_package_dir(name, requirements)
|
||||
@ -88,6 +93,7 @@ def update_core_packages(only_check=False, silent=False):
|
||||
continue
|
||||
if not silent or pm.outdated(pkg_dir, requirements):
|
||||
pm.update(name, requirements, only_check=only_check)
|
||||
return True
|
||||
|
||||
|
||||
def pioplus_call(args, **kwargs):
|
||||
@ -99,8 +105,11 @@ def pioplus_call(args, **kwargs):
|
||||
sys.version.split()[0]))
|
||||
|
||||
pioplus_path = join(get_core_package_dir("tool-pioplus"), "pioplus")
|
||||
os.environ['PYTHONEXEPATH'] = util.get_pythonexe_path()
|
||||
os.environ['PYTHONPYSITEDIR'] = get_core_package_dir("pysite-pioplus")
|
||||
pythonexe_path = util.get_pythonexe_path()
|
||||
os.environ['PYTHONEXEPATH'] = pythonexe_path
|
||||
os.environ['PYTHONPYSITEDIR'] = get_core_package_dir("contrib-pysite")
|
||||
os.environ['PATH'] = (os.pathsep).join(
|
||||
[dirname(pythonexe_path), os.environ['PATH']])
|
||||
util.copy_pythonpath_to_osenv()
|
||||
code = subprocess.call([pioplus_path] + args, **kwargs)
|
||||
|
||||
@ -124,3 +133,5 @@ def pioplus_call(args, **kwargs):
|
||||
|
||||
if code != 0:
|
||||
raise exception.ReturnErrorCode(1)
|
||||
|
||||
return True
|
||||
|
@ -21,7 +21,6 @@ from os.path import isdir, join
|
||||
|
||||
import arrow
|
||||
import click
|
||||
import semantic_version
|
||||
|
||||
from platformio import app, commands, exception, util
|
||||
from platformio.managers.package import BasePkgManager
|
||||
@ -71,7 +70,10 @@ class LibraryManager(BasePkgManager):
|
||||
del manifest['sentence']
|
||||
|
||||
if "author" in manifest:
|
||||
manifest['authors'] = [{"name": manifest['author']}]
|
||||
if isinstance(manifest['author'], dict):
|
||||
manifest['authors'] = [manifest['author']]
|
||||
else:
|
||||
manifest['authors'] = [{"name": manifest['author']}]
|
||||
del manifest['author']
|
||||
|
||||
if "authors" in manifest and not isinstance(manifest['authors'], list):
|
||||
@ -101,6 +103,7 @@ class LibraryManager(BasePkgManager):
|
||||
"sam": "atmelsam",
|
||||
"samd": "atmelsam",
|
||||
"esp8266": "espressif8266",
|
||||
"esp32": "espressif32",
|
||||
"arc32": "intel_arc32"
|
||||
}
|
||||
for arch in manifest['architectures'].split(","):
|
||||
@ -149,8 +152,7 @@ class LibraryManager(BasePkgManager):
|
||||
]
|
||||
return items
|
||||
|
||||
@staticmethod
|
||||
def max_satisfying_repo_version(versions, requirements=None):
|
||||
def max_satisfying_repo_version(self, versions, requirements=None):
|
||||
|
||||
def _cmp_dates(datestr1, datestr2):
|
||||
date1 = arrow.get(datestr1)
|
||||
@ -159,29 +161,22 @@ class LibraryManager(BasePkgManager):
|
||||
return 0
|
||||
return -1 if date1 < date2 else 1
|
||||
|
||||
semver_spec = self.parse_semver_spec(
|
||||
requirements) if requirements else None
|
||||
item = None
|
||||
reqspec = None
|
||||
if requirements:
|
||||
try:
|
||||
reqspec = semantic_version.Spec(requirements)
|
||||
except ValueError:
|
||||
pass
|
||||
for v in versions:
|
||||
specver = None
|
||||
try:
|
||||
specver = semantic_version.Version(v['name'], partial=True)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if reqspec:
|
||||
if not specver or specver not in reqspec:
|
||||
for v in versions:
|
||||
semver_new = self.parse_semver_version(v['name'])
|
||||
if semver_spec:
|
||||
if not semver_new or semver_new not in semver_spec:
|
||||
continue
|
||||
if not item or semantic_version.Version(
|
||||
item['name'], partial=True) < specver:
|
||||
if not item or self.parse_semver_version(
|
||||
item['name']) < semver_new:
|
||||
item = v
|
||||
elif requirements:
|
||||
if requirements == v['name']:
|
||||
return v
|
||||
|
||||
else:
|
||||
if not item or _cmp_dates(item['released'],
|
||||
v['released']) == -1:
|
||||
@ -193,7 +188,7 @@ class LibraryManager(BasePkgManager):
|
||||
util.get_api_result(
|
||||
"/lib/info/%d" % self.get_pkg_id_by_name(
|
||||
name, requirements, silent=silent),
|
||||
cache_valid="1d")['versions'], requirements)
|
||||
cache_valid="1h")['versions'], requirements)
|
||||
return item['name'] if item else None
|
||||
|
||||
def get_pkg_id_by_name(self,
|
||||
@ -236,11 +231,11 @@ class LibraryManager(BasePkgManager):
|
||||
requirements=None,
|
||||
silent=False,
|
||||
trigger_event=True,
|
||||
interactive=False):
|
||||
interactive=False,
|
||||
force=False):
|
||||
pkg_dir = None
|
||||
try:
|
||||
_name, _requirements, _url = self.parse_pkg_input(
|
||||
name, requirements)
|
||||
_name, _requirements, _url = self.parse_pkg_uri(name, requirements)
|
||||
if not _url:
|
||||
name = "id=%d" % self.get_pkg_id_by_name(
|
||||
_name,
|
||||
@ -248,15 +243,20 @@ class LibraryManager(BasePkgManager):
|
||||
silent=silent,
|
||||
interactive=interactive)
|
||||
requirements = _requirements
|
||||
pkg_dir = BasePkgManager.install(self, name, requirements, silent,
|
||||
trigger_event)
|
||||
pkg_dir = BasePkgManager.install(
|
||||
self,
|
||||
name,
|
||||
requirements,
|
||||
silent=silent,
|
||||
trigger_event=trigger_event,
|
||||
force=force)
|
||||
except exception.InternetIsOffline as e:
|
||||
if not silent:
|
||||
click.secho(str(e), fg="yellow")
|
||||
return
|
||||
return None
|
||||
|
||||
if not pkg_dir:
|
||||
return
|
||||
return None
|
||||
|
||||
manifest = self.load_manifest(pkg_dir)
|
||||
if "dependencies" not in manifest:
|
||||
@ -268,7 +268,12 @@ class LibraryManager(BasePkgManager):
|
||||
for filters in self.normalize_dependencies(manifest['dependencies']):
|
||||
assert "name" in filters
|
||||
if any([s in filters.get("version", "") for s in ("\\", "/")]):
|
||||
self.install("{name}={version}".format(**filters))
|
||||
self.install(
|
||||
"{name}={version}".format(**filters),
|
||||
silent=silent,
|
||||
trigger_event=trigger_event,
|
||||
interactive=interactive,
|
||||
force=force)
|
||||
else:
|
||||
try:
|
||||
lib_info = self.search_for_library(filters, silent,
|
||||
@ -281,14 +286,18 @@ class LibraryManager(BasePkgManager):
|
||||
if filters.get("version"):
|
||||
self.install(
|
||||
lib_info['id'],
|
||||
requirements=filters.get("version"),
|
||||
filters.get("version"),
|
||||
silent=silent,
|
||||
trigger_event=trigger_event)
|
||||
trigger_event=trigger_event,
|
||||
interactive=interactive,
|
||||
force=force)
|
||||
else:
|
||||
self.install(
|
||||
lib_info['id'],
|
||||
silent=silent,
|
||||
trigger_event=trigger_event)
|
||||
trigger_event=trigger_event,
|
||||
interactive=interactive,
|
||||
force=force)
|
||||
return pkg_dir
|
||||
|
||||
@staticmethod
|
||||
@ -315,7 +324,7 @@ class LibraryManager(BasePkgManager):
|
||||
|
||||
lib_info = None
|
||||
result = util.get_api_result(
|
||||
"/v2/lib/search", dict(query=" ".join(query)), cache_valid="3d")
|
||||
"/v2/lib/search", dict(query=" ".join(query)), cache_valid="1h")
|
||||
if result['total'] == 1:
|
||||
lib_info = result['items'][0]
|
||||
elif result['total'] > 1:
|
||||
|
@ -30,7 +30,7 @@ from platformio.downloader import FileDownloader
|
||||
from platformio.unpacker import FileUnpacker
|
||||
from platformio.vcsclient import VCSClientFactory
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
# pylint: disable=too-many-arguments, too-many-return-statements
|
||||
|
||||
|
||||
class PackageRepoIterator(object):
|
||||
@ -78,9 +78,15 @@ class PkgRepoMixin(object):
|
||||
PIO_VERSION = semantic_version.Version(util.pepver_to_semver(__version__))
|
||||
|
||||
@staticmethod
|
||||
def max_satisfying_repo_version(versions, requirements=None):
|
||||
def is_system_compatible(valid_systems):
|
||||
if valid_systems in (None, "all", "*"):
|
||||
return True
|
||||
if not isinstance(valid_systems, list):
|
||||
valid_systems = list([valid_systems])
|
||||
return util.get_systype() in valid_systems
|
||||
|
||||
def max_satisfying_repo_version(self, versions, requirements=None):
|
||||
item = None
|
||||
systype = util.get_systype()
|
||||
reqspec = None
|
||||
if requirements:
|
||||
try:
|
||||
@ -89,8 +95,7 @@ class PkgRepoMixin(object):
|
||||
pass
|
||||
|
||||
for v in versions:
|
||||
if "system" in v and v['system'] not in ("all", "*") and \
|
||||
systype not in v['system']:
|
||||
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(
|
||||
@ -121,8 +126,9 @@ class PkgRepoMixin(object):
|
||||
def get_all_repo_versions(self, name):
|
||||
result = []
|
||||
for versions in PackageRepoIterator(name, self.repositories):
|
||||
result.extend([v['version'] for v in versions])
|
||||
return sorted(set(result))
|
||||
result.extend(
|
||||
[semantic_version.Version(v['version']) for v in versions])
|
||||
return [str(v) for v in sorted(set(result))]
|
||||
|
||||
|
||||
class PkgInstallerMixin(object):
|
||||
@ -187,15 +193,36 @@ class PkgInstallerMixin(object):
|
||||
|
||||
@staticmethod
|
||||
def unpack(source_path, dest_dir):
|
||||
fu = FileUnpacker(source_path, dest_dir)
|
||||
return fu.start()
|
||||
with FileUnpacker(source_path) as fu:
|
||||
return fu.unpack(dest_dir)
|
||||
|
||||
@staticmethod
|
||||
def parse_semver_spec(value, raise_exception=False):
|
||||
try:
|
||||
return semantic_version.Spec(value)
|
||||
except ValueError as e:
|
||||
if raise_exception:
|
||||
raise e
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def parse_semver_version(value, raise_exception=False):
|
||||
try:
|
||||
try:
|
||||
return semantic_version.Version(value)
|
||||
except ValueError:
|
||||
return semantic_version.Version.coerce(value)
|
||||
except ValueError as e:
|
||||
if raise_exception:
|
||||
raise e
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_install_dirname(manifest):
|
||||
name = re.sub(r"[^\da-z\_\-\. ]", "_", manifest['name'], flags=re.I)
|
||||
if "id" in manifest:
|
||||
name += "_ID%d" % manifest['id']
|
||||
return name
|
||||
return str(name)
|
||||
|
||||
def get_src_manifest_path(self, pkg_dir):
|
||||
if not isdir(pkg_dir):
|
||||
@ -258,7 +285,7 @@ class PkgInstallerMixin(object):
|
||||
if "version" not in manifest:
|
||||
manifest['version'] = "0.0.0"
|
||||
|
||||
manifest['__pkg_dir'] = pkg_dir
|
||||
manifest['__pkg_dir'] = util.path_to_unicode(pkg_dir)
|
||||
self.cache_set(cache_key, manifest)
|
||||
return manifest
|
||||
|
||||
@ -283,21 +310,23 @@ class PkgInstallerMixin(object):
|
||||
continue
|
||||
elif not pkg_id and manifest['name'] != name:
|
||||
continue
|
||||
elif not PkgRepoMixin.is_system_compatible(manifest.get("system")):
|
||||
continue
|
||||
|
||||
# strict version or VCS HASH
|
||||
if requirements and requirements == manifest['version']:
|
||||
return manifest
|
||||
|
||||
try:
|
||||
if requirements and not semantic_version.Spec(
|
||||
requirements).match(
|
||||
semantic_version.Version(
|
||||
manifest['version'], partial=True)):
|
||||
if requirements and not self.parse_semver_spec(
|
||||
requirements, raise_exception=True).match(
|
||||
self.parse_semver_version(
|
||||
manifest['version'], raise_exception=True)):
|
||||
continue
|
||||
elif not best or (semantic_version.Version(
|
||||
manifest['version'], partial=True) >
|
||||
semantic_version.Version(
|
||||
best['version'], partial=True)):
|
||||
elif not best or (self.parse_semver_version(
|
||||
manifest['version'], raise_exception=True) >
|
||||
self.parse_semver_version(
|
||||
best['version'], raise_exception=True)):
|
||||
best = manifest
|
||||
except ValueError:
|
||||
pass
|
||||
@ -383,7 +412,7 @@ class PkgInstallerMixin(object):
|
||||
finally:
|
||||
if isdir(tmp_dir):
|
||||
util.rmtree_(tmp_dir)
|
||||
return
|
||||
return None
|
||||
|
||||
def _update_src_manifest(self, data, src_dir):
|
||||
if not isdir(src_dir):
|
||||
@ -405,16 +434,10 @@ class PkgInstallerMixin(object):
|
||||
pkg_dir = join(self.package_dir, pkg_dirname)
|
||||
cur_manifest = self.load_manifest(pkg_dir)
|
||||
|
||||
tmp_semver = None
|
||||
tmp_semver = self.parse_semver_version(tmp_manifest['version'])
|
||||
cur_semver = None
|
||||
try:
|
||||
tmp_semver = semantic_version.Version(
|
||||
tmp_manifest['version'], partial=True)
|
||||
if cur_manifest:
|
||||
cur_semver = semantic_version.Version(
|
||||
cur_manifest['version'], partial=True)
|
||||
except ValueError:
|
||||
pass
|
||||
if cur_manifest:
|
||||
cur_semver = self.parse_semver_version(cur_manifest['version'])
|
||||
|
||||
# package should satisfy requirements
|
||||
if requirements:
|
||||
@ -490,51 +513,57 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
|
||||
click.echo("%s: %s" % (self.__class__.__name__, message), nl=nl)
|
||||
|
||||
@staticmethod
|
||||
def parse_pkg_input( # pylint: disable=too-many-branches
|
||||
def parse_pkg_uri( # pylint: disable=too-many-branches
|
||||
text, requirements=None):
|
||||
text = str(text)
|
||||
# git@github.com:user/package.git
|
||||
url_marker = text[:4]
|
||||
if url_marker not in ("git@", "git+") or ":" not in text:
|
||||
url_marker = "://"
|
||||
name, url = None, None
|
||||
|
||||
# Parse requirements
|
||||
req_conditions = [
|
||||
not requirements,
|
||||
"@" in text,
|
||||
not url_marker.startswith("git")
|
||||
] # yapf: disable
|
||||
"@" in text, not requirements, ":" not in text
|
||||
or text.rfind("/") < text.rfind("@")
|
||||
]
|
||||
if all(req_conditions):
|
||||
text, requirements = text.rsplit("@", 1)
|
||||
|
||||
# Handle PIO Library Registry ID
|
||||
if text.isdigit():
|
||||
text = "id=" + text
|
||||
# Parse custom name
|
||||
elif "=" in text and not text.startswith("id="):
|
||||
name, text = text.split("=", 1)
|
||||
|
||||
name, url = (None, text)
|
||||
if "=" in text and not text.startswith("id="):
|
||||
name, url = text.split("=", 1)
|
||||
# Parse URL
|
||||
# if valid URL with scheme vcs+protocol://
|
||||
if "+" in text and text.find("+") < text.find("://"):
|
||||
url = text
|
||||
elif "/" in text or "\\" in text:
|
||||
git_conditions = [
|
||||
# Handle GitHub URL (https://github.com/user/package)
|
||||
text.startswith("https://github.com/") and not text.endswith(
|
||||
(".zip", ".tar.gz")),
|
||||
(text.split("#", 1)[0]
|
||||
if "#" in text else text).endswith(".git")
|
||||
]
|
||||
hg_conditions = [
|
||||
# Handle Developer Mbed URL
|
||||
# (https://developer.mbed.org/users/user/code/package/)
|
||||
text.startswith("https://developer.mbed.org")
|
||||
]
|
||||
if any(git_conditions):
|
||||
url = "git+" + text
|
||||
elif any(hg_conditions):
|
||||
url = "hg+" + text
|
||||
elif "://" not in text and (isfile(text) or isdir(text)):
|
||||
url = "file://" + text
|
||||
elif "://" in text:
|
||||
url = text
|
||||
# Handle short version of GitHub URL
|
||||
elif text.count("/") == 1:
|
||||
url = "git+https://github.com/" + text
|
||||
|
||||
git_conditions = [
|
||||
# Handle GitHub URL (https://github.com/user/package)
|
||||
url.startswith("https://github.com/") and not url.endswith(
|
||||
(".zip", ".tar.gz")),
|
||||
url.startswith("http")
|
||||
and (url.split("#", 1)[0] if "#" in url else url).endswith(".git")
|
||||
]
|
||||
if any(git_conditions):
|
||||
url = "git+" + url
|
||||
|
||||
# Handle Developer Mbed URL
|
||||
# (https://developer.mbed.org/users/user/code/package/)
|
||||
if url.startswith("https://developer.mbed.org"):
|
||||
url = "hg+" + url
|
||||
|
||||
if any([s in url for s in ("\\", "/")]) and url_marker not in url:
|
||||
if isfile(url) or isdir(url):
|
||||
url = "file://" + url
|
||||
elif url.count("/") == 1 and "git" not in url_marker:
|
||||
url = "git+https://github.com/" + url
|
||||
|
||||
# determine name
|
||||
if url_marker in url and not name:
|
||||
# Parse name from URL
|
||||
if url and not name:
|
||||
_url = url.split("#", 1)[0] if "#" in url else url
|
||||
if _url.endswith(("\\", "/")):
|
||||
_url = _url[:-1]
|
||||
@ -542,8 +571,6 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
|
||||
if "." in name and not name.startswith("."):
|
||||
name = name.rsplit(".", 1)[0]
|
||||
|
||||
if url_marker not in url:
|
||||
url = None
|
||||
return (name or text, requirements, url)
|
||||
|
||||
def outdated(self, pkg_dir, requirements=None):
|
||||
@ -553,11 +580,12 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
|
||||
`False` - package is up-to-date
|
||||
`String` - a found latest version
|
||||
"""
|
||||
assert isdir(pkg_dir)
|
||||
if not isdir(pkg_dir):
|
||||
return None
|
||||
latest = None
|
||||
manifest = self.load_manifest(pkg_dir)
|
||||
# skip a fixed package to a specific version
|
||||
if "@" in pkg_dir and "__src_url" not in manifest:
|
||||
# skip fixed package to a specific version
|
||||
if "@" in pkg_dir and "__src_url" not in manifest and not requirements:
|
||||
return None
|
||||
|
||||
if "__src_url" in manifest:
|
||||
@ -585,8 +613,10 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
|
||||
up_to_date = False
|
||||
try:
|
||||
assert "__src_url" not in manifest
|
||||
up_to_date = (semantic_version.Version.coerce(manifest['version'])
|
||||
>= semantic_version.Version.coerce(latest))
|
||||
up_to_date = (self.parse_semver_version(
|
||||
manifest['version'], raise_exception=True) >=
|
||||
self.parse_semver_version(
|
||||
latest, raise_exception=True))
|
||||
except (AssertionError, ValueError):
|
||||
up_to_date = latest == manifest['version']
|
||||
|
||||
@ -596,18 +626,22 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
|
||||
name,
|
||||
requirements=None,
|
||||
silent=False,
|
||||
trigger_event=True):
|
||||
trigger_event=True,
|
||||
force=False):
|
||||
name, requirements, url = self.parse_pkg_uri(name, requirements)
|
||||
package_dir = self.get_package_dir(name, requirements, url)
|
||||
|
||||
# avoid circle dependencies
|
||||
if not self.INSTALL_HISTORY:
|
||||
self.INSTALL_HISTORY = []
|
||||
history_key = "%s-%s" % (name, requirements) if requirements else name
|
||||
history_key = "%s-%s-%s" % (name, requirements or "", url or "")
|
||||
if history_key in self.INSTALL_HISTORY:
|
||||
return
|
||||
return package_dir
|
||||
self.INSTALL_HISTORY.append(history_key)
|
||||
|
||||
name, requirements, url = self.parse_pkg_input(name, requirements)
|
||||
package_dir = self.get_package_dir(name, requirements, url)
|
||||
if package_dir and force:
|
||||
self.uninstall(package_dir)
|
||||
package_dir = None
|
||||
|
||||
if not package_dir or not silent:
|
||||
msg = "Installing " + click.style(name, fg="cyan")
|
||||
@ -652,8 +686,7 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
|
||||
if isdir(package):
|
||||
pkg_dir = package
|
||||
else:
|
||||
name, requirements, url = self.parse_pkg_input(
|
||||
package, requirements)
|
||||
name, requirements, url = self.parse_pkg_uri(package, requirements)
|
||||
pkg_dir = self.get_package_dir(name, requirements, url)
|
||||
|
||||
if not pkg_dir:
|
||||
@ -689,15 +722,11 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
|
||||
label=manifest['name'])
|
||||
return True
|
||||
|
||||
def update( # pylint: disable=too-many-return-statements
|
||||
self,
|
||||
package,
|
||||
requirements=None,
|
||||
only_check=False):
|
||||
def update(self, package, requirements=None, only_check=False):
|
||||
if isdir(package):
|
||||
pkg_dir = package
|
||||
else:
|
||||
pkg_dir = self.get_package_dir(*self.parse_pkg_input(package))
|
||||
pkg_dir = self.get_package_dir(*self.parse_pkg_uri(package))
|
||||
|
||||
if not pkg_dir:
|
||||
raise exception.UnknownPackage("%s @ %s" % (package,
|
||||
@ -713,7 +742,7 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
|
||||
nl=False)
|
||||
if not util.internet_on():
|
||||
click.echo("[%s]" % (click.style("Off-line", fg="yellow")))
|
||||
return
|
||||
return None
|
||||
|
||||
latest = self.outdated(pkg_dir, requirements)
|
||||
if latest:
|
||||
@ -721,10 +750,10 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
|
||||
elif latest is False:
|
||||
click.echo("[%s]" % (click.style("Up-to-date", fg="green")))
|
||||
else:
|
||||
click.echo("[%s]" % (click.style("Skip", fg="yellow")))
|
||||
click.echo("[%s]" % (click.style("Fixed", fg="yellow")))
|
||||
|
||||
if only_check or not latest:
|
||||
return
|
||||
return True
|
||||
|
||||
if "__src_url" in manifest:
|
||||
vcs = VCSClientFactory.newClient(pkg_dir, manifest['__src_url'])
|
||||
|
@ -18,6 +18,7 @@ import re
|
||||
from imp import load_source
|
||||
from multiprocessing import cpu_count
|
||||
from os.path import basename, dirname, isdir, isfile, join
|
||||
from urllib import quote
|
||||
|
||||
import click
|
||||
import semantic_version
|
||||
@ -63,9 +64,10 @@ class PlatformManager(BasePkgManager):
|
||||
skip_default_package=False,
|
||||
trigger_event=True,
|
||||
silent=False,
|
||||
force=False,
|
||||
**_): # pylint: disable=too-many-arguments, arguments-differ
|
||||
platform_dir = BasePkgManager.install(
|
||||
self, name, requirements, silent=silent)
|
||||
self, name, requirements, silent=silent, force=force)
|
||||
p = PlatformFactory.newPlatform(platform_dir)
|
||||
|
||||
# @Hook: when 'update' operation (trigger_event is False),
|
||||
@ -76,7 +78,8 @@ class PlatformManager(BasePkgManager):
|
||||
with_packages,
|
||||
without_packages,
|
||||
skip_default_package,
|
||||
silent=silent)
|
||||
silent=silent,
|
||||
force=force)
|
||||
self.cleanup_packages(p.packages.keys())
|
||||
return True
|
||||
|
||||
@ -84,10 +87,12 @@ class PlatformManager(BasePkgManager):
|
||||
if isdir(package):
|
||||
pkg_dir = package
|
||||
else:
|
||||
name, requirements, url = self.parse_pkg_input(
|
||||
package, requirements)
|
||||
name, requirements, url = self.parse_pkg_uri(package, requirements)
|
||||
pkg_dir = self.get_package_dir(name, requirements, url)
|
||||
|
||||
if not pkg_dir:
|
||||
raise exception.UnknownPlatform(package)
|
||||
|
||||
p = PlatformFactory.newPlatform(pkg_dir)
|
||||
BasePkgManager.uninstall(self, pkg_dir, requirements)
|
||||
|
||||
@ -108,25 +113,28 @@ class PlatformManager(BasePkgManager):
|
||||
if isdir(package):
|
||||
pkg_dir = package
|
||||
else:
|
||||
name, requirements, url = self.parse_pkg_input(
|
||||
package, requirements)
|
||||
name, requirements, url = self.parse_pkg_uri(package, requirements)
|
||||
pkg_dir = self.get_package_dir(name, requirements, url)
|
||||
|
||||
p = PlatformFactory.newPlatform(pkg_dir)
|
||||
pkgs_before = pkgs_after = p.get_installed_packages().keys()
|
||||
if not pkg_dir:
|
||||
raise exception.UnknownPlatform(package)
|
||||
|
||||
p = PlatformFactory.newPlatform(pkg_dir)
|
||||
pkgs_before = p.get_installed_packages().keys()
|
||||
|
||||
missed_pkgs = set()
|
||||
if not only_packages:
|
||||
BasePkgManager.update(self, pkg_dir, requirements, only_check)
|
||||
p = PlatformFactory.newPlatform(pkg_dir)
|
||||
pkgs_after = p.get_installed_packages().keys()
|
||||
missed_pkgs = set(pkgs_before) & set(p.packages.keys())
|
||||
missed_pkgs -= set(p.get_installed_packages().keys())
|
||||
|
||||
p.update_packages(only_check)
|
||||
self.cleanup_packages(p.packages.keys())
|
||||
|
||||
pkgs_missed = set(pkgs_before) - set(pkgs_after)
|
||||
if pkgs_missed:
|
||||
if missed_pkgs:
|
||||
p.install_packages(
|
||||
with_packages=pkgs_missed, skip_default_package=True)
|
||||
with_packages=list(missed_pkgs), skip_default_package=True)
|
||||
|
||||
return True
|
||||
|
||||
@ -164,7 +172,19 @@ class PlatformManager(BasePkgManager):
|
||||
@staticmethod
|
||||
@util.memoized
|
||||
def get_registered_boards():
|
||||
return util.get_api_result("/boards", cache_valid="30d")
|
||||
return util.get_api_result("/boards", cache_valid="7d")
|
||||
|
||||
def get_all_boards(self):
|
||||
boards = self.get_installed_boards()
|
||||
know_boards = ["%s:%s" % (b['platform'], b['id']) for b in boards]
|
||||
try:
|
||||
for board in self.get_registered_boards():
|
||||
key = "%s:%s" % (board['platform'], board['id'])
|
||||
if key not in know_boards:
|
||||
boards.append(board)
|
||||
except (exception.APIRequestError, exception.InternetIsOffline):
|
||||
pass
|
||||
return sorted(boards, key=lambda b: b['name'])
|
||||
|
||||
def board_config(self, id_, platform=None):
|
||||
for manifest in self.get_installed_boards():
|
||||
@ -197,18 +217,19 @@ class PlatformFactory(object):
|
||||
|
||||
@classmethod
|
||||
def newPlatform(cls, name, requirements=None):
|
||||
pm = PlatformManager()
|
||||
platform_dir = None
|
||||
if isdir(name):
|
||||
platform_dir = name
|
||||
name = PlatformManager().load_manifest(platform_dir)['name']
|
||||
name = pm.load_manifest(platform_dir)['name']
|
||||
elif name.endswith("platform.json") and isfile(name):
|
||||
platform_dir = dirname(name)
|
||||
name = util.load_json(name)['name']
|
||||
else:
|
||||
if not requirements and "@" in name:
|
||||
name, requirements = name.rsplit("@", 1)
|
||||
platform_dir = PlatformManager().get_package_dir(
|
||||
name, requirements)
|
||||
name, requirements, url = pm.parse_pkg_uri(name, requirements)
|
||||
platform_dir = pm.get_package_dir(name, requirements, url)
|
||||
if platform_dir:
|
||||
name = pm.load_manifest(platform_dir)['name']
|
||||
|
||||
if not platform_dir:
|
||||
raise exception.UnknownPlatform(name if not requirements else
|
||||
@ -230,11 +251,13 @@ class PlatformFactory(object):
|
||||
|
||||
class PlatformPackagesMixin(object):
|
||||
|
||||
def install_packages(self,
|
||||
with_packages=None,
|
||||
without_packages=None,
|
||||
skip_default_package=False,
|
||||
silent=False):
|
||||
def install_packages( # pylint: disable=too-many-arguments
|
||||
self,
|
||||
with_packages=None,
|
||||
without_packages=None,
|
||||
skip_default_package=False,
|
||||
silent=False,
|
||||
force=False):
|
||||
with_packages = set(self.find_pkg_names(with_packages or []))
|
||||
without_packages = set(self.find_pkg_names(without_packages or []))
|
||||
|
||||
@ -249,14 +272,11 @@ class PlatformPackagesMixin(object):
|
||||
continue
|
||||
elif (name in with_packages or
|
||||
not (skip_default_package or opts.get("optional", False))):
|
||||
if self.is_valid_requirements(version):
|
||||
self.pm.install(name, version, silent=silent)
|
||||
else:
|
||||
requirements = None
|
||||
if "@" in version:
|
||||
version, requirements = version.rsplit("@", 1)
|
||||
if ":" in version:
|
||||
self.pm.install(
|
||||
"%s=%s" % (name, version), requirements, silent=silent)
|
||||
"%s=%s" % (name, version), silent=silent, force=force)
|
||||
else:
|
||||
self.pm.install(name, version, silent=silent, force=force)
|
||||
|
||||
return True
|
||||
|
||||
@ -279,10 +299,10 @@ class PlatformPackagesMixin(object):
|
||||
|
||||
def update_packages(self, only_check=False):
|
||||
for name, manifest in self.get_installed_packages().items():
|
||||
version = self.packages[name].get("version", "")
|
||||
if "@" in version:
|
||||
_, version = version.rsplit("@", 1)
|
||||
self.pm.update(manifest['__pkg_dir'], version, only_check)
|
||||
requirements = self.packages[name].get("version", "")
|
||||
if ":" in requirements:
|
||||
_, requirements, __ = self.pm.parse_pkg_uri(requirements)
|
||||
self.pm.update(manifest['__pkg_dir'], requirements, only_check)
|
||||
|
||||
def get_installed_packages(self):
|
||||
items = {}
|
||||
@ -294,18 +314,19 @@ class PlatformPackagesMixin(object):
|
||||
|
||||
def are_outdated_packages(self):
|
||||
for name, manifest in self.get_installed_packages().items():
|
||||
version = self.packages[name].get("version", "")
|
||||
if "@" in version:
|
||||
_, version = version.rsplit("@", 1)
|
||||
if self.pm.outdated(manifest['__pkg_dir'], version):
|
||||
requirements = self.packages[name].get("version", "")
|
||||
if ":" in requirements:
|
||||
_, requirements, __ = self.pm.parse_pkg_uri(requirements)
|
||||
if self.pm.outdated(manifest['__pkg_dir'], requirements):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_package_dir(self, name):
|
||||
version = self.packages[name].get("version", "")
|
||||
if self.is_valid_requirements(version):
|
||||
return self.pm.get_package_dir(name, version)
|
||||
return self.pm.get_package_dir(*self._parse_pkg_input(name, 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(name, version)
|
||||
|
||||
def get_package_version(self, name):
|
||||
pkg_dir = self.get_package_dir(name)
|
||||
@ -313,16 +334,6 @@ class PlatformPackagesMixin(object):
|
||||
return None
|
||||
return self.pm.load_manifest(pkg_dir).get("version")
|
||||
|
||||
@staticmethod
|
||||
def is_valid_requirements(requirements):
|
||||
return requirements and "://" not in requirements
|
||||
|
||||
def _parse_pkg_input(self, name, version):
|
||||
requirements = None
|
||||
if "@" in version:
|
||||
version, requirements = version.rsplit("@", 1)
|
||||
return self.pm.parse_pkg_input("%s=%s" % (name, version), requirements)
|
||||
|
||||
|
||||
class PlatformRunMixin(object):
|
||||
|
||||
@ -384,6 +395,12 @@ class PlatformRunMixin(object):
|
||||
is_error = self.LINE_ERROR_RE.search(line) is not None
|
||||
self._echo_line(line, level=3 if is_error else 2)
|
||||
|
||||
a_pos = line.find("fatal error:")
|
||||
b_pos = line.rfind(": No such file or directory")
|
||||
if a_pos == -1 or b_pos == -1:
|
||||
return
|
||||
self._echo_missed_dependency(line[a_pos + 12:b_pos].strip())
|
||||
|
||||
def _echo_line(self, line, level):
|
||||
if line.startswith("scons: "):
|
||||
line = line[7:]
|
||||
@ -395,6 +412,27 @@ class PlatformRunMixin(object):
|
||||
fg = "green"
|
||||
click.secho(line, fg=fg, err=level > 1)
|
||||
|
||||
@staticmethod
|
||||
def _echo_missed_dependency(filename):
|
||||
if "/" in filename or not filename.endswith((".h", ".hpp")):
|
||||
return
|
||||
banner = """
|
||||
{dots}
|
||||
* Looking for {filename_styled} dependency? Check our library registry!
|
||||
*
|
||||
* CLI > platformio lib search "header:{filename}"
|
||||
* Web > {link}
|
||||
*
|
||||
{dots}
|
||||
""".format(filename=filename,
|
||||
filename_styled=click.style(filename, fg="cyan"),
|
||||
link=click.style(
|
||||
"http://platformio.org/lib/search?query=header:%s" % quote(
|
||||
filename, safe=""),
|
||||
fg="blue"),
|
||||
dots="*" * (55 + len(filename)))
|
||||
click.echo(banner, err=True)
|
||||
|
||||
@staticmethod
|
||||
def get_job_nums():
|
||||
try:
|
||||
@ -498,8 +536,8 @@ class PlatformBase( # pylint: disable=too-many-public-methods
|
||||
config = PlatformBoardConfig(manifest_path)
|
||||
if "platform" in config and config.get("platform") != self.name:
|
||||
return
|
||||
elif ("platforms" in config
|
||||
and self.name not in config.get("platforms")):
|
||||
elif "platforms" in config \
|
||||
and self.name not in config.get("platforms"):
|
||||
return
|
||||
config.manifest['platform'] = self.name
|
||||
self._BOARDS_CACHE[board_id] = config
|
||||
@ -637,24 +675,37 @@ class PlatformBoardConfig(object):
|
||||
|
||||
def get_brief_data(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self._manifest['name'],
|
||||
"platform": self._manifest.get("platform"),
|
||||
"mcu": self._manifest.get("build", {}).get("mcu", "").upper(),
|
||||
"id":
|
||||
self.id,
|
||||
"name":
|
||||
self._manifest['name'],
|
||||
"platform":
|
||||
self._manifest.get("platform"),
|
||||
"mcu":
|
||||
self._manifest.get("build", {}).get("mcu", "").upper(),
|
||||
"fcpu":
|
||||
int(self._manifest.get("build", {}).get("f_cpu", "0L")[:-1]),
|
||||
"ram": self._manifest.get("upload", {}).get("maximum_ram_size", 0),
|
||||
"rom": self._manifest.get("upload", {}).get("maximum_size", 0),
|
||||
"connectivity": self._manifest.get("connectivity"),
|
||||
"frameworks": self._manifest.get("frameworks"),
|
||||
"debug": self.get_debug_data(),
|
||||
"vendor": self._manifest['vendor'],
|
||||
"url": self._manifest['url']
|
||||
int(
|
||||
re.sub(r"[^\d]+", "",
|
||||
self._manifest.get("build", {}).get("f_cpu", "0L"))),
|
||||
"ram":
|
||||
self._manifest.get("upload", {}).get("maximum_ram_size", 0),
|
||||
"rom":
|
||||
self._manifest.get("upload", {}).get("maximum_size", 0),
|
||||
"connectivity":
|
||||
self._manifest.get("connectivity"),
|
||||
"frameworks":
|
||||
self._manifest.get("frameworks"),
|
||||
"debug":
|
||||
self.get_debug_data(),
|
||||
"vendor":
|
||||
self._manifest['vendor'],
|
||||
"url":
|
||||
self._manifest['url']
|
||||
}
|
||||
|
||||
def get_debug_data(self):
|
||||
if not self._manifest.get("debug", {}).get("tools"):
|
||||
return
|
||||
return None
|
||||
tools = {}
|
||||
for name, options in self._manifest['debug']['tools'].items():
|
||||
tools[name] = {}
|
||||
|
@ -15,10 +15,11 @@
|
||||
import atexit
|
||||
import platform
|
||||
import Queue
|
||||
import sys
|
||||
import re
|
||||
import threading
|
||||
from collections import deque
|
||||
from os import getenv
|
||||
from os import getenv, sep
|
||||
from os.path import join
|
||||
from time import sleep, time
|
||||
from traceback import format_exc
|
||||
|
||||
@ -109,7 +110,7 @@ class MeasurementProtocol(TelemetryBase):
|
||||
self['cd1'] = util.get_systype()
|
||||
self['cd2'] = "Python/%s %s" % (platform.python_version(),
|
||||
platform.platform())
|
||||
self['cd3'] = " ".join(_filter_args(sys.argv[1:]))
|
||||
# self['cd3'] = " ".join(_filter_args(sys.argv[1:]))
|
||||
self['cd4'] = 1 if (not util.is_ci()
|
||||
and (caller_id or not util.is_container())) else 0
|
||||
if caller_id:
|
||||
@ -314,14 +315,29 @@ def on_event(category, action, label=None, value=None, screen_name=None):
|
||||
|
||||
|
||||
def on_exception(e):
|
||||
|
||||
def _cleanup_description(text):
|
||||
text = text.replace("Traceback (most recent call last):", "")
|
||||
text = re.sub(
|
||||
r'File "([^"]+)"',
|
||||
lambda m: join(*m.group(1).split(sep)[-2:]),
|
||||
text,
|
||||
flags=re.M)
|
||||
text = re.sub(r"\s+", " ", text, flags=re.M)
|
||||
return text.strip()
|
||||
|
||||
skip_conditions = [
|
||||
isinstance(e, cls)
|
||||
for cls in (IOError, exception.AbortedByUser,
|
||||
exception.NotGlobalLibDir, exception.InternetIsOffline,
|
||||
for cls in (IOError, exception.ReturnErrorCode,
|
||||
exception.AbortedByUser, exception.NotGlobalLibDir,
|
||||
exception.InternetIsOffline,
|
||||
exception.NotPlatformIOProject,
|
||||
exception.UserSideException)
|
||||
]
|
||||
skip_conditions.append("[API] Account: " in str(e))
|
||||
try:
|
||||
skip_conditions.append("[API] Account: " in str(e))
|
||||
except UnicodeEncodeError as ue:
|
||||
e = ue
|
||||
if any(skip_conditions):
|
||||
return
|
||||
is_crash = any([
|
||||
@ -329,8 +345,8 @@ def on_exception(e):
|
||||
"Error" in e.__class__.__name__
|
||||
])
|
||||
mp = MeasurementProtocol()
|
||||
mp['exd'] = ("%s: %s" % (type(e).__name__, format_exc()
|
||||
if is_crash else e))[:2048]
|
||||
description = _cleanup_description(format_exc() if is_crash else str(e))
|
||||
mp['exd'] = ("%s: %s" % (type(e).__name__, description))[:2048]
|
||||
mp['exf'] = 1 if is_crash else 0
|
||||
mp.send("exception")
|
||||
|
||||
@ -391,3 +407,4 @@ def resend_backuped_reports():
|
||||
# clean
|
||||
tm['backup'] = []
|
||||
app.set_state_item("telemetry", tm)
|
||||
return True
|
||||
|
@ -13,7 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
from os import chmod
|
||||
from os.path import join, splitext
|
||||
from os.path import join
|
||||
from tarfile import open as tarfile_open
|
||||
from time import mktime
|
||||
from zipfile import ZipFile
|
||||
@ -39,6 +39,9 @@ class ArchiveBase(object):
|
||||
def after_extract(self, item, dest_dir):
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
self._afo.close()
|
||||
|
||||
|
||||
class TARArchive(ArchiveBase):
|
||||
|
||||
@ -76,28 +79,32 @@ class ZIPArchive(ArchiveBase):
|
||||
|
||||
class FileUnpacker(object):
|
||||
|
||||
def __init__(self, archpath, dest_dir="."):
|
||||
self._archpath = archpath
|
||||
self._dest_dir = dest_dir
|
||||
def __init__(self, archpath):
|
||||
self.archpath = archpath
|
||||
self._unpacker = None
|
||||
|
||||
_, archext = splitext(archpath.lower())
|
||||
if archext in (".gz", ".bz2"):
|
||||
self._unpacker = TARArchive(archpath)
|
||||
elif archext == ".zip":
|
||||
self._unpacker = ZIPArchive(archpath)
|
||||
|
||||
def __enter__(self):
|
||||
if self.archpath.lower().endswith((".gz", ".bz2")):
|
||||
self._unpacker = TARArchive(self.archpath)
|
||||
elif self.archpath.lower().endswith(".zip"):
|
||||
self._unpacker = ZIPArchive(self.archpath)
|
||||
if not self._unpacker:
|
||||
raise UnsupportedArchiveType(archpath)
|
||||
raise UnsupportedArchiveType(self.archpath)
|
||||
return self
|
||||
|
||||
def start(self):
|
||||
def __exit__(self, *args):
|
||||
if self._unpacker:
|
||||
self._unpacker.close()
|
||||
|
||||
def unpack(self, dest_dir="."):
|
||||
assert self._unpacker
|
||||
if app.is_disabled_progressbar():
|
||||
click.echo("Unpacking...")
|
||||
for item in self._unpacker.get_items():
|
||||
self._unpacker.extract_item(item, self._dest_dir)
|
||||
self._unpacker.extract_item(item, dest_dir)
|
||||
else:
|
||||
items = self._unpacker.get_items()
|
||||
with click.progressbar(items, label="Unpacking") as pb:
|
||||
for item in pb:
|
||||
self._unpacker.extract_item(item, self._dest_dir)
|
||||
self._unpacker.extract_item(item, dest_dir)
|
||||
return True
|
||||
|
@ -22,13 +22,13 @@ import socket
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from functools import wraps
|
||||
from glob import glob
|
||||
from os.path import (abspath, basename, dirname, expanduser, isdir, isfile,
|
||||
join, normpath, splitdrive)
|
||||
from shutil import rmtree
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
from time import sleep, time
|
||||
|
||||
import click
|
||||
import requests
|
||||
@ -149,6 +149,25 @@ class memoized(object):
|
||||
self.cache = {}
|
||||
|
||||
|
||||
class throttle(object):
|
||||
|
||||
def __init__(self, threshhold):
|
||||
self.threshhold = threshhold # milliseconds
|
||||
self.last = 0
|
||||
|
||||
def __call__(self, fn):
|
||||
|
||||
@wraps(fn)
|
||||
def wrapper(*args, **kwargs):
|
||||
diff = int(round((time() - self.last) * 1000))
|
||||
if diff < self.threshhold:
|
||||
sleep((self.threshhold - diff) * 0.001)
|
||||
self.last = time()
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def singleton(cls):
|
||||
""" From PEP-318 http://www.python.org/dev/peps/pep-0318/#examples """
|
||||
_instances = {}
|
||||
@ -161,12 +180,8 @@ def singleton(cls):
|
||||
return get_instance
|
||||
|
||||
|
||||
@contextmanager
|
||||
def capture_stdout(output):
|
||||
stdout = sys.stdout
|
||||
sys.stdout = output
|
||||
yield
|
||||
sys.stdout = stdout
|
||||
def path_to_unicode(path):
|
||||
return path.decode(sys.getfilesystemencoding()).encode("utf-8")
|
||||
|
||||
|
||||
def load_json(file_path):
|
||||
@ -281,6 +296,11 @@ def get_projectsrc_dir():
|
||||
return get_project_optional_dir("src_dir", join(get_project_dir(), "src"))
|
||||
|
||||
|
||||
def get_projectinclude_dir():
|
||||
return get_project_optional_dir("include_dir",
|
||||
join(get_project_dir(), "include"))
|
||||
|
||||
|
||||
def get_projecttest_dir():
|
||||
return get_project_optional_dir("test_dir", join(get_project_dir(),
|
||||
"test"))
|
||||
@ -317,11 +337,10 @@ def get_projectdata_dir():
|
||||
|
||||
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)
|
||||
path = join(path or get_project_dir(), "platformio.ini")
|
||||
if not isfile(path):
|
||||
raise exception.NotPlatformIOProject(
|
||||
dirname(path) if path.endswith("platformio.ini") else path)
|
||||
cp = ProjectConfig()
|
||||
cp.read(path)
|
||||
return cp
|
||||
@ -336,8 +355,8 @@ def parse_conf_multi_values(items):
|
||||
]
|
||||
|
||||
|
||||
def change_filemtime(path, time):
|
||||
os.utime(path, (time, time))
|
||||
def change_filemtime(path, mtime):
|
||||
os.utime(path, (mtime, mtime))
|
||||
|
||||
|
||||
def is_ci():
|
||||
@ -398,7 +417,7 @@ def copy_pythonpath_to_osenv():
|
||||
os.environ['PYTHONPATH'] = os.pathsep.join(_PYTHONPATH)
|
||||
|
||||
|
||||
def get_serialports(filter_hwid=False):
|
||||
def get_serial_ports(filter_hwid=False):
|
||||
try:
|
||||
from serial.tools.list_ports import comports
|
||||
except ImportError:
|
||||
@ -426,29 +445,117 @@ def get_serialports(filter_hwid=False):
|
||||
return result
|
||||
|
||||
|
||||
def get_logicaldisks():
|
||||
disks = []
|
||||
def get_logical_devices():
|
||||
items = []
|
||||
if platform.system() == "Windows":
|
||||
result = exec_command(
|
||||
["wmic", "logicaldisk", "get", "name,VolumeName"]).get("out", "")
|
||||
disknamere = re.compile(r"^([A-Z]{1}\:)\s*(\S+)?")
|
||||
for line in result.split("\n"):
|
||||
match = disknamere.match(line.strip())
|
||||
if not match:
|
||||
continue
|
||||
disks.append({"disk": match.group(1), "name": match.group(2)})
|
||||
try:
|
||||
result = exec_command(
|
||||
["wmic", "logicaldisk", "get", "name,VolumeName"]).get(
|
||||
"out", "")
|
||||
devicenamere = re.compile(r"^([A-Z]{1}\:)\s*(\S+)?")
|
||||
for line in result.split("\n"):
|
||||
match = devicenamere.match(line.strip())
|
||||
if not match:
|
||||
continue
|
||||
items.append({
|
||||
"path": match.group(1) + "\\",
|
||||
"name": match.group(2)
|
||||
})
|
||||
return items
|
||||
except WindowsError: # pylint: disable=undefined-variable
|
||||
pass
|
||||
# try "fsutil"
|
||||
result = exec_command(["fsutil", "fsinfo", "drives"]).get("out", "")
|
||||
for device in re.findall(r"[A-Z]:\\", result):
|
||||
items.append({"path": device, "name": None})
|
||||
return items
|
||||
else:
|
||||
result = exec_command(["df"]).get("out")
|
||||
disknamere = re.compile(r"\d+\%\s+([a-z\d\-_/]+)$", flags=re.I)
|
||||
devicenamere = re.compile(r"^/.+\d+\%\s+([a-z\d\-_/]+)$", flags=re.I)
|
||||
for line in result.split("\n"):
|
||||
match = disknamere.search(line.strip())
|
||||
match = devicenamere.match(line.strip())
|
||||
if not match:
|
||||
continue
|
||||
disks.append({
|
||||
"disk": match.group(1),
|
||||
items.append({
|
||||
"path": match.group(1),
|
||||
"name": basename(match.group(1))
|
||||
})
|
||||
return disks
|
||||
return items
|
||||
|
||||
|
||||
### Backward compatibility for PIO Core <3.5
|
||||
get_serialports = get_serial_ports
|
||||
get_logicaldisks = lambda: [{
|
||||
"disk": d['path'],
|
||||
"name": d['name']
|
||||
} for d in get_logical_devices()]
|
||||
|
||||
|
||||
def get_mdns_services():
|
||||
try:
|
||||
import zeroconf
|
||||
except ImportError:
|
||||
from site import addsitedir
|
||||
from platformio.managers.core import get_core_package_dir
|
||||
contrib_pysite_dir = get_core_package_dir("contrib-pysite")
|
||||
addsitedir(contrib_pysite_dir)
|
||||
sys.path.insert(0, contrib_pysite_dir)
|
||||
import zeroconf
|
||||
|
||||
class mDNSListener(object):
|
||||
|
||||
def __init__(self):
|
||||
self._zc = zeroconf.Zeroconf(
|
||||
interfaces=zeroconf.InterfaceChoice.All)
|
||||
self._found_types = []
|
||||
self._found_services = []
|
||||
|
||||
def __enter__(self):
|
||||
zeroconf.ServiceBrowser(self._zc, "_services._dns-sd._udp.local.",
|
||||
self)
|
||||
return self
|
||||
|
||||
def __exit__(self, etype, value, traceback):
|
||||
self._zc.close()
|
||||
|
||||
def remove_service(self, zc, type_, name):
|
||||
pass
|
||||
|
||||
def add_service(self, zc, type_, name):
|
||||
try:
|
||||
assert zeroconf.service_type_name(name)
|
||||
assert str(name)
|
||||
except (AssertionError, UnicodeError,
|
||||
zeroconf.BadTypeInNameException):
|
||||
return
|
||||
if name not in self._found_types:
|
||||
self._found_types.append(name)
|
||||
zeroconf.ServiceBrowser(self._zc, name, self)
|
||||
if type_ in self._found_types:
|
||||
s = zc.get_service_info(type_, name)
|
||||
if s:
|
||||
self._found_services.append(s)
|
||||
|
||||
def get_services(self):
|
||||
return self._found_services
|
||||
|
||||
items = []
|
||||
with mDNSListener() as mdns:
|
||||
sleep(3)
|
||||
for service in mdns.get_services():
|
||||
items.append({
|
||||
"type":
|
||||
service.type,
|
||||
"name":
|
||||
service.name,
|
||||
"ip":
|
||||
".".join([str(ord(c)) for c in service.address]),
|
||||
"port":
|
||||
service.port,
|
||||
"properties":
|
||||
service.properties
|
||||
})
|
||||
return items
|
||||
|
||||
|
||||
def get_request_defheaders():
|
||||
@ -461,6 +568,7 @@ def _api_request_session():
|
||||
return requests.Session()
|
||||
|
||||
|
||||
@throttle(500)
|
||||
def _get_api_result(
|
||||
url, # pylint: disable=too-many-branches
|
||||
params=None,
|
||||
@ -470,7 +578,7 @@ def _get_api_result(
|
||||
|
||||
result = None
|
||||
r = None
|
||||
disable_ssl_check = sys.version_info < (2, 7, 9)
|
||||
verify_ssl = sys.version_info >= (2, 7, 9)
|
||||
|
||||
headers = get_request_defheaders()
|
||||
if not url.startswith("http"):
|
||||
@ -486,14 +594,14 @@ def _get_api_result(
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
verify=not disable_ssl_check)
|
||||
verify=verify_ssl)
|
||||
else:
|
||||
r = _api_request_session().get(
|
||||
url,
|
||||
params=params,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
verify=not disable_ssl_check)
|
||||
verify=verify_ssl)
|
||||
result = r.json()
|
||||
r.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
@ -513,6 +621,7 @@ def _get_api_result(
|
||||
|
||||
|
||||
def get_api_result(url, params=None, data=None, auth=None, cache_valid=None):
|
||||
internet_on(raise_exception=True)
|
||||
from platformio.app import ContentCache
|
||||
total = 0
|
||||
max_retries = 5
|
||||
@ -532,8 +641,6 @@ def get_api_result(url, params=None, data=None, auth=None, cache_valid=None):
|
||||
return result
|
||||
except (requests.exceptions.ConnectionError,
|
||||
requests.exceptions.Timeout) as e:
|
||||
if not internet_on():
|
||||
raise exception.InternetIsOffline()
|
||||
from platformio.maintenance import in_silence
|
||||
total += 1
|
||||
if not in_silence():
|
||||
@ -548,18 +655,38 @@ def get_api_result(url, params=None, data=None, auth=None, cache_valid=None):
|
||||
"Please try later.")
|
||||
|
||||
|
||||
def internet_on(timeout=3):
|
||||
PING_INTERNET_IPS = [
|
||||
"192.30.253.113", # github.com
|
||||
"159.122.18.156", # dl.bintray.com
|
||||
"193.222.52.25" # dl.platformio.org
|
||||
]
|
||||
|
||||
|
||||
@memoized
|
||||
def _internet_on():
|
||||
timeout = 2
|
||||
socket.setdefaulttimeout(timeout)
|
||||
for host in ("dl.bintray.com", "dl.platformio.org"):
|
||||
for ip in PING_INTERNET_IPS:
|
||||
try:
|
||||
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host,
|
||||
80))
|
||||
if os.getenv("HTTP_PROXY", os.getenv("HTTPS_PROXY")):
|
||||
requests.get(
|
||||
"http://%s" % ip, allow_redirects=False, timeout=timeout)
|
||||
else:
|
||||
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((ip,
|
||||
80))
|
||||
return True
|
||||
except: # pylint: disable=bare-except
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def internet_on(raise_exception=False):
|
||||
result = _internet_on()
|
||||
if raise_exception and not result:
|
||||
raise exception.InternetIsOffline()
|
||||
return result
|
||||
|
||||
|
||||
def get_pythonexe_path():
|
||||
return os.environ.get("PYTHONEXEPATH", normpath(sys.executable))
|
||||
|
||||
@ -596,8 +723,13 @@ def pepver_to_semver(pepver):
|
||||
def rmtree_(path):
|
||||
|
||||
def _onerror(_, name, __):
|
||||
os.chmod(name, stat.S_IWRITE)
|
||||
os.remove(name)
|
||||
try:
|
||||
os.chmod(name, stat.S_IWRITE)
|
||||
os.remove(name)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
click.secho(
|
||||
"Please manually remove file `%s`" % name, fg="red", err=True)
|
||||
raise e
|
||||
|
||||
return rmtree(path, onerror=_onerror)
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
import re
|
||||
from os.path import join
|
||||
from subprocess import check_call
|
||||
from subprocess import CalledProcessError, check_call
|
||||
from sys import modules
|
||||
from urlparse import urlparse
|
||||
|
||||
@ -29,8 +29,9 @@ class VCSClientFactory(object):
|
||||
result = urlparse(remote_url)
|
||||
type_ = result.scheme
|
||||
tag = None
|
||||
if not type_ and remote_url.startswith("git@"):
|
||||
if not type_ and remote_url.startswith("git+"):
|
||||
type_ = "git"
|
||||
remote_url = remote_url[4:]
|
||||
elif "+" in result.scheme:
|
||||
type_, _ = result.scheme.split("+", 1)
|
||||
remote_url = remote_url[len(type_) + 1:]
|
||||
@ -93,7 +94,12 @@ class VCSClientBase(object):
|
||||
args = [self.command] + args
|
||||
if "cwd" not in kwargs:
|
||||
kwargs['cwd'] = self.src_dir
|
||||
return check_call(args, **kwargs) == 0
|
||||
try:
|
||||
check_call(args, **kwargs)
|
||||
return True
|
||||
except CalledProcessError as e:
|
||||
raise PlatformioException(
|
||||
"VCS: Could not process command %s" % e.cmd)
|
||||
|
||||
def get_cmd_output(self, args, **kwargs):
|
||||
args = [self.command] + args
|
||||
@ -111,6 +117,13 @@ class GitClient(VCSClientBase):
|
||||
|
||||
command = "git"
|
||||
|
||||
def check_client(self):
|
||||
try:
|
||||
return VCSClientBase.check_client(self)
|
||||
except UserSideException:
|
||||
raise UserSideException(
|
||||
"Please install Git client from https://git-scm.com/downloads")
|
||||
|
||||
def get_branches(self):
|
||||
output = self.get_cmd_output(["branch"])
|
||||
output = output.replace("*", "") # fix active branch
|
||||
|
@ -91,3 +91,7 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="0451", ATTRS{idProduct}=="f432", MODE="0666
|
||||
|
||||
# CMSIS-DAP compatible adapters
|
||||
ATTRS{product}=="*CMSIS-DAP*", MODE="664", GROUP="plugdev"
|
||||
|
||||
# Black Magic Probe
|
||||
SUBSYSTEM=="tty", ATTRS{interface}=="Black Magic GDB Server"
|
||||
SUBSYSTEM=="tty", ATTRS{interface}=="Black Magic UART Port"
|
||||
|
@ -335,7 +335,7 @@ Boards
|
||||
|
||||
vendors = {}
|
||||
for data in BOARDS:
|
||||
frameworks = data['frameworks']
|
||||
frameworks = data['frameworks'] or []
|
||||
vendor = data['vendor']
|
||||
if type_ in frameworks:
|
||||
if vendor in vendors:
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import site
|
||||
import sys
|
||||
from platform import system
|
||||
from tempfile import NamedTemporaryFile
|
||||
@ -26,39 +27,34 @@ def fix_winpython_pathenv():
|
||||
"""
|
||||
Add Python & Python Scripts to the search path on Windows
|
||||
"""
|
||||
import ctypes
|
||||
from ctypes.wintypes import HWND, UINT, WPARAM, LPARAM, LPVOID
|
||||
try:
|
||||
import _winreg as winreg
|
||||
except ImportError:
|
||||
import winreg
|
||||
|
||||
# took these lines from the native "win_add2path.py"
|
||||
pythonpath = os.path.dirname(CURINTERPRETER_PATH)
|
||||
pythonpath = os.path.dirname(os.path.normpath(sys.executable))
|
||||
scripts = os.path.join(pythonpath, "Scripts")
|
||||
if not os.path.isdir(scripts):
|
||||
os.makedirs(scripts)
|
||||
appdata = os.environ["APPDATA"]
|
||||
if hasattr(site, "USER_SITE"):
|
||||
userpath = site.USER_SITE.replace(appdata, "%APPDATA%")
|
||||
userscripts = os.path.join(userpath, "Scripts")
|
||||
else:
|
||||
userscripts = None
|
||||
|
||||
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, u"Environment") as key:
|
||||
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, "Environment") as key:
|
||||
try:
|
||||
envpath = winreg.QueryValueEx(key, u"PATH")[0]
|
||||
envpath = winreg.QueryValueEx(key, "PATH")[0]
|
||||
except WindowsError:
|
||||
envpath = u"%PATH%"
|
||||
|
||||
paths = [envpath]
|
||||
for path in (pythonpath, scripts):
|
||||
for path in (pythonpath, scripts, userscripts):
|
||||
if path and path not in envpath and os.path.isdir(path):
|
||||
paths.append(path)
|
||||
|
||||
envpath = os.pathsep.join(paths)
|
||||
winreg.SetValueEx(key, u"PATH", 0, winreg.REG_EXPAND_SZ, envpath)
|
||||
winreg.ExpandEnvironmentStrings(envpath)
|
||||
|
||||
# notify the system about the changes
|
||||
SendMessage = ctypes.windll.user32.SendMessageW
|
||||
SendMessage.argtypes = HWND, UINT, WPARAM, LPVOID
|
||||
SendMessage.restype = LPARAM
|
||||
SendMessage(0xFFFF, 0x1A, 0, u"Environment")
|
||||
winreg.SetValueEx(key, "PATH", 0, winreg.REG_EXPAND_SZ, envpath)
|
||||
return True
|
||||
|
||||
|
||||
@ -92,6 +88,10 @@ def exec_python_cmd(args):
|
||||
|
||||
|
||||
def install_pip():
|
||||
r = exec_python_cmd(["-m", "pip", "--version"])
|
||||
if r['returncode'] == 0:
|
||||
print r['out']
|
||||
return
|
||||
try:
|
||||
from urllib2 import urlopen
|
||||
except ImportError:
|
||||
@ -112,16 +112,16 @@ def install_pip():
|
||||
|
||||
def install_platformio():
|
||||
r = None
|
||||
cmd = ["pip", "install", "-U", "platformio"]
|
||||
cmd = ["-m", "pip", "install", "-U", "platformio"]
|
||||
# cmd = [
|
||||
# "pip", "install", "-U",
|
||||
# "-m", "pip", "install", "-U",
|
||||
# "https://github.com/platformio/platformio-core/archive/develop.zip"
|
||||
# ]
|
||||
try:
|
||||
r = exec_python_cmd(cmd)
|
||||
assert r['returncode'] == 0
|
||||
except AssertionError:
|
||||
cmd.insert(1, "--no-cache-dir")
|
||||
cmd.insert(2, "--no-cache-dir")
|
||||
r = exec_python_cmd(cmd)
|
||||
if r:
|
||||
print_exec_result(r)
|
||||
|
4
setup.py
4
setup.py
@ -18,14 +18,14 @@ from platformio import (__author__, __description__, __email__, __license__,
|
||||
__title__, __url__, __version__)
|
||||
|
||||
install_requires = [
|
||||
"arrow<1",
|
||||
"arrow>=0.10.0,!=0.11.0",
|
||||
"bottle<0.13",
|
||||
"click>=5,<6",
|
||||
"colorama",
|
||||
"lockfile>=0.9.1,<0.13",
|
||||
"pyserial>=3,<4,!=3.3",
|
||||
"requests>=2.4.0,<3",
|
||||
"semantic_version>=2.5.0"
|
||||
"semantic_version>=2.5.0,<3"
|
||||
]
|
||||
|
||||
setup(
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
import json
|
||||
import re
|
||||
from os.path import basename
|
||||
|
||||
from platformio import exception, util
|
||||
from platformio.commands.init import cli as cmd_init
|
||||
@ -39,7 +38,7 @@ def test_global_install_registry(clirunner, validate_cliresult,
|
||||
result = clirunner.invoke(cmd_lib, [
|
||||
"-g", "install", "58", "547@2.2.4", "DallasTemperature",
|
||||
"http://dl.platformio.org/libraries/archives/3/5174.tar.gz",
|
||||
"ArduinoJson@5.6.7", "ArduinoJson@~5.7.0", "1089@fee16e880b"
|
||||
"ArduinoJson@5.6.7", "ArduinoJson@~5.7.0", "168@00589a3250"
|
||||
])
|
||||
validate_cliresult(result)
|
||||
|
||||
@ -65,7 +64,7 @@ def test_global_install_registry(clirunner, validate_cliresult,
|
||||
items2 = [
|
||||
"ArduinoJson_ID64", "ArduinoJson_ID64@5.6.7", "DallasTemperature_ID54",
|
||||
"DHT22_ID58", "ESPAsyncTCP_ID305", "NeoPixelBus_ID547", "OneWire_ID1",
|
||||
"IRremoteESP8266_ID1089"
|
||||
"EspSoftwareSerial_ID168"
|
||||
]
|
||||
assert set(items1) == set(items2)
|
||||
|
||||
@ -134,7 +133,7 @@ def test_global_install_repository(clirunner, validate_cliresult,
|
||||
assert "is already installed" in result.output
|
||||
|
||||
|
||||
def test_global_lib_list(clirunner, validate_cliresult, isolated_pio_home):
|
||||
def test_global_lib_list(clirunner, validate_cliresult):
|
||||
result = clirunner.invoke(cmd_lib, ["-g", "list"])
|
||||
validate_cliresult(result)
|
||||
assert all([n in result.output for n in ("OneWire", "DHT22", "64")])
|
||||
@ -142,31 +141,30 @@ def test_global_lib_list(clirunner, validate_cliresult, isolated_pio_home):
|
||||
result = clirunner.invoke(cmd_lib, ["-g", "list", "--json-output"])
|
||||
assert all([
|
||||
n in result.output
|
||||
for n in ("PJON", "git+https://github.com/knolleary/pubsubclient",
|
||||
"https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip"
|
||||
)
|
||||
for n in (
|
||||
"PJON", "git+https://github.com/knolleary/pubsubclient",
|
||||
"https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip")
|
||||
])
|
||||
items1 = [i['name'] for i in json.loads(result.output)]
|
||||
items2 = [
|
||||
"OneWire", "DHT22", "PJON", "ESPAsyncTCP", "ArduinoJson",
|
||||
"PubSubClient", "rs485-nodeproto", "Adafruit ST7735 Library",
|
||||
"RadioHead-1.62", "DallasTemperature", "NeoPixelBus",
|
||||
"IRremoteESP8266", "platformio-libmirror"
|
||||
"EspSoftwareSerial", "platformio-libmirror"
|
||||
]
|
||||
assert set(items1) == set(items2)
|
||||
|
||||
|
||||
def test_global_lib_update_check(clirunner, validate_cliresult,
|
||||
isolated_pio_home):
|
||||
def test_global_lib_update_check(clirunner, validate_cliresult):
|
||||
result = clirunner.invoke(
|
||||
cmd_lib, ["-g", "update", "--only-check", "--json-output"])
|
||||
validate_cliresult(result)
|
||||
output = json.loads(result.output)
|
||||
assert set(["ArduinoJson", "IRremoteESP8266", "NeoPixelBus"]) == set(
|
||||
[l['name'] for l in output])
|
||||
assert set(["ArduinoJson", "EspSoftwareSerial",
|
||||
"NeoPixelBus"]) == set([l['name'] for l in output])
|
||||
|
||||
|
||||
def test_global_lib_update(clirunner, validate_cliresult, isolated_pio_home):
|
||||
def test_global_lib_update(clirunner, validate_cliresult):
|
||||
# update library using package directory
|
||||
result = clirunner.invoke(
|
||||
cmd_lib,
|
||||
@ -184,10 +182,10 @@ def test_global_lib_update(clirunner, validate_cliresult, isolated_pio_home):
|
||||
result = clirunner.invoke(cmd_lib, ["-g", "update"])
|
||||
validate_cliresult(result)
|
||||
validate_cliresult(result)
|
||||
assert result.output.count("[Skip]") == 5
|
||||
assert result.output.count("[Fixed]") == 5
|
||||
assert result.output.count("[Up-to-date]") == 10
|
||||
assert "Uninstalling ArduinoJson @ 5.7.3" in result.output
|
||||
assert "Uninstalling IRremoteESP8266 @ fee16e880b" in result.output
|
||||
assert "Uninstalling EspSoftwareSerial @ 00589a3250" in result.output
|
||||
|
||||
# update unknown library
|
||||
result = clirunner.invoke(cmd_lib, ["-g", "update", "Unknown"])
|
||||
@ -208,14 +206,14 @@ def test_global_lib_uninstall(clirunner, validate_cliresult,
|
||||
|
||||
# uninstall the rest libraries
|
||||
result = clirunner.invoke(cmd_lib, [
|
||||
"-g", "uninstall", "1", "ArduinoJson@!=5.6.7",
|
||||
"https://github.com/bblanchon/ArduinoJson.git", "IRremoteESP8266@>=0.2"
|
||||
"-g", "uninstall", "1", "https://github.com/bblanchon/ArduinoJson.git",
|
||||
"ArduinoJson@!=5.6.7", "EspSoftwareSerial@>=3.3.1"
|
||||
])
|
||||
validate_cliresult(result)
|
||||
|
||||
items1 = [d.basename for d in isolated_pio_home.join("lib").listdir()]
|
||||
items2 = [
|
||||
"ArduinoJson", "ArduinoJson_ID64@5.6.7", "DallasTemperature_ID54",
|
||||
"ArduinoJson_ID64", "ArduinoJson_ID64@5.6.7", "DallasTemperature_ID54",
|
||||
"DHT22_ID58", "ESPAsyncTCP_ID305", "NeoPixelBus_ID547", "PJON",
|
||||
"PJON@src-79de467ebe19de18287becff0a1fb42d", "PubSubClient",
|
||||
"RadioHead-1.62", "rs485-nodeproto", "platformio-libmirror"
|
||||
@ -228,7 +226,7 @@ def test_global_lib_uninstall(clirunner, validate_cliresult,
|
||||
assert isinstance(result.exception, exception.UnknownPackage)
|
||||
|
||||
|
||||
def test_lib_show(clirunner, validate_cliresult, isolated_pio_home):
|
||||
def test_lib_show(clirunner, validate_cliresult):
|
||||
result = clirunner.invoke(cmd_lib, ["show", "64"])
|
||||
validate_cliresult(result)
|
||||
assert all(
|
||||
@ -238,14 +236,14 @@ def test_lib_show(clirunner, validate_cliresult, isolated_pio_home):
|
||||
assert "OneWire" in result.output
|
||||
|
||||
|
||||
def test_lib_builtin(clirunner, validate_cliresult, isolated_pio_home):
|
||||
def test_lib_builtin(clirunner, validate_cliresult):
|
||||
result = clirunner.invoke(cmd_lib, ["builtin"])
|
||||
validate_cliresult(result)
|
||||
result = clirunner.invoke(cmd_lib, ["builtin", "--json-output"])
|
||||
validate_cliresult(result)
|
||||
|
||||
|
||||
def test_lib_stats(clirunner, validate_cliresult, isolated_pio_home):
|
||||
def test_lib_stats(clirunner, validate_cliresult):
|
||||
result = clirunner.invoke(cmd_lib, ["stats"])
|
||||
validate_cliresult(result)
|
||||
assert all([
|
||||
|
@ -54,11 +54,11 @@ def test_install_unknown_from_registry(clirunner, validate_cliresult,
|
||||
def test_install_known_version(clirunner, validate_cliresult,
|
||||
isolated_pio_home):
|
||||
result = clirunner.invoke(cli_platform.platform_install, [
|
||||
"atmelavr@1.1.0", "--skip-default-package", "--with-package",
|
||||
"atmelavr@1.2.0", "--skip-default-package", "--with-package",
|
||||
"tool-avrdude"
|
||||
])
|
||||
validate_cliresult(result)
|
||||
assert "atmelavr @ 1.1.0" in result.output
|
||||
assert "atmelavr @ 1.2.0" in result.output
|
||||
assert "Installing tool-avrdude @" in result.output
|
||||
assert len(isolated_pio_home.join("packages").listdir()) == 1
|
||||
|
||||
@ -69,7 +69,7 @@ def test_install_from_vcs(clirunner, validate_cliresult, isolated_pio_home):
|
||||
"platform-espressif8266.git#feature/stage", "--skip-default-package"
|
||||
])
|
||||
validate_cliresult(result)
|
||||
assert "espressif8266_stage" in result.output
|
||||
assert "espressif8266" in result.output
|
||||
|
||||
|
||||
def test_list_json_output(clirunner, validate_cliresult, isolated_pio_home):
|
||||
@ -79,14 +79,14 @@ def test_list_json_output(clirunner, validate_cliresult, isolated_pio_home):
|
||||
assert isinstance(list_result, list)
|
||||
assert len(list_result)
|
||||
platforms = [item['name'] for item in list_result]
|
||||
assert set(["atmelavr", "espressif8266_stage"]) == set(platforms)
|
||||
assert set(["atmelavr", "espressif8266"]) == set(platforms)
|
||||
|
||||
|
||||
def test_list_raw_output(clirunner, validate_cliresult, isolated_pio_home):
|
||||
result = clirunner.invoke(cli_platform.platform_list)
|
||||
validate_cliresult(result)
|
||||
assert all(
|
||||
[s in result.output for s in ("atmelavr", "espressif8266_stage")])
|
||||
[s in result.output for s in ("atmelavr", "espressif8266")])
|
||||
|
||||
|
||||
def test_update_check(clirunner, validate_cliresult, isolated_pio_home):
|
||||
@ -102,13 +102,13 @@ def test_update_check(clirunner, validate_cliresult, isolated_pio_home):
|
||||
def test_update_raw(clirunner, validate_cliresult, isolated_pio_home):
|
||||
result = clirunner.invoke(cli_platform.platform_update)
|
||||
validate_cliresult(result)
|
||||
assert "Uninstalling atmelavr @ 1.1.0:" in result.output
|
||||
assert "Uninstalling atmelavr @ 1.2.0:" in result.output
|
||||
assert "PlatformManager: Installing atmelavr @" in result.output
|
||||
assert len(isolated_pio_home.join("packages").listdir()) == 1
|
||||
|
||||
|
||||
def test_uninstall(clirunner, validate_cliresult, isolated_pio_home):
|
||||
result = clirunner.invoke(cli_platform.platform_uninstall,
|
||||
["atmelavr", "espressif8266_stage"])
|
||||
["atmelavr", "espressif8266"])
|
||||
validate_cliresult(result)
|
||||
assert len(isolated_pio_home.join("platforms").listdir()) == 0
|
||||
|
@ -25,8 +25,8 @@ def test_pkg_input_parser():
|
||||
[("PkgName", "!=1.2.3,<2.0"), ("PkgName", "!=1.2.3,<2.0", None)],
|
||||
["PkgName@1.2.3", ("PkgName", "1.2.3", None)],
|
||||
[("PkgName@1.2.3", "1.2.5"), ("PkgName@1.2.3", "1.2.5", None)],
|
||||
["id:13", ("id:13", None, None)],
|
||||
["id:13@~1.2.3", ("id:13", "~1.2.3", None)],
|
||||
["id=13", ("id=13", None, None)],
|
||||
["id=13@~1.2.3", ("id=13", "~1.2.3", None)],
|
||||
[
|
||||
util.get_home_dir(),
|
||||
(".platformio", None, "file://" + util.get_home_dir())
|
||||
@ -117,11 +117,15 @@ def test_pkg_input_parser():
|
||||
],
|
||||
[
|
||||
"git@github.com:user/package.git",
|
||||
("package", None, "git@github.com:user/package.git")
|
||||
("package", None, "git+git@github.com:user/package.git")
|
||||
],
|
||||
[
|
||||
"git@github.com:user/package.git#v1.2.0",
|
||||
("package", None, "git@github.com:user/package.git#v1.2.0")
|
||||
("package", None, "git+git@github.com:user/package.git#v1.2.0")
|
||||
],
|
||||
[
|
||||
"LocalName=git@github.com:user/package.git#v1.2.0@~1.2.0",
|
||||
("LocalName", "~1.2.0", "git+git@github.com:user/package.git#v1.2.0")
|
||||
],
|
||||
[
|
||||
"git+ssh://git@gitlab.private-server.com/user/package#1.2.0",
|
||||
@ -132,13 +136,19 @@ def test_pkg_input_parser():
|
||||
"git+ssh://user@gitlab.private-server.com:1234/package#1.2.0",
|
||||
("package", None,
|
||||
"git+ssh://user@gitlab.private-server.com:1234/package#1.2.0")
|
||||
],
|
||||
[
|
||||
"LocalName=git+ssh://user@gitlab.private-server.com:1234"
|
||||
"/package#1.2.0@!=13",
|
||||
("LocalName", "!=13",
|
||||
"git+ssh://user@gitlab.private-server.com:1234/package#1.2.0")
|
||||
]
|
||||
]
|
||||
for params, result in items:
|
||||
if isinstance(params, tuple):
|
||||
assert PackageManager.parse_pkg_input(*params) == result
|
||||
assert PackageManager.parse_pkg_uri(*params) == result
|
||||
else:
|
||||
assert PackageManager.parse_pkg_input(params) == result
|
||||
assert PackageManager.parse_pkg_uri(params) == result
|
||||
|
||||
|
||||
def test_install_packages(isolated_pio_home, tmpdir):
|
||||
@ -146,7 +156,7 @@ def test_install_packages(isolated_pio_home, tmpdir):
|
||||
dict(id=1, name="name_1", version="shasum"),
|
||||
dict(id=1, name="name_1", version="2.0.0"),
|
||||
dict(id=1, name="name_1", version="2.1.0"),
|
||||
dict(id=1, name="name_1", version="1.2.0"),
|
||||
dict(id=1, name="name_1", version="1.2"),
|
||||
dict(id=1, name="name_1", version="1.0.0"),
|
||||
dict(name="name_2", version="1.0.0"),
|
||||
dict(name="name_2", version="2.0.0",
|
||||
@ -167,7 +177,7 @@ def test_install_packages(isolated_pio_home, tmpdir):
|
||||
assert len(pm.get_installed()) == len(packages) - 1
|
||||
|
||||
pkg_dirnames = [
|
||||
'name_1_ID1', 'name_1_ID1@1.0.0', 'name_1_ID1@1.2.0',
|
||||
'name_1_ID1', 'name_1_ID1@1.0.0', 'name_1_ID1@1.2',
|
||||
'name_1_ID1@2.0.0', 'name_1_ID1@shasum', 'name_2',
|
||||
'name_2@src-177cbce1f0705580d17790fda1cc2ef5',
|
||||
'name_2@src-f863b537ab00f4c7b5011fc44b120e1f'
|
||||
@ -182,12 +192,11 @@ def test_get_package(isolated_pio_home):
|
||||
[("1", ), None],
|
||||
[("id=1", "shasum"), dict(id=1, name="name_1", version="shasum")],
|
||||
[("id=1", "*"), dict(id=1, name="name_1", version="2.1.0")],
|
||||
[("id=1", "^1"), dict(id=1, name="name_1", version="1.2.0")],
|
||||
[("id=1", "^1"), dict(id=1, name="name_1", version="1.2.0")],
|
||||
[("name_1", "<2"), dict(id=1, name="name_1", version="1.2.0")],
|
||||
[("id=1", "^1"), dict(id=1, name="name_1", version="1.2")],
|
||||
[("id=1", "^1"), dict(id=1, name="name_1", version="1.2")],
|
||||
[("name_1", "<2"), dict(id=1, name="name_1", version="1.2")],
|
||||
[("name_1", ">2"), None],
|
||||
[("name_1", "2-0-0"), dict(id=1, name="name_1", version="2.1.0")],
|
||||
[("name_1", "2-0-0"), dict(id=1, name="name_1", version="2.1.0")],
|
||||
[("name_1", "2-0-0"), None],
|
||||
[("name_2", ), dict(name="name_2", version="4.0.0")],
|
||||
[("url_has_higher_priority", None, "git+https://github.com"),
|
||||
dict(name="name_2", version="2.0.0",
|
||||
|
22
tests/test_misc.py
Normal file
22
tests/test_misc.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||
#
|
||||
# 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.
|
||||
|
||||
import requests
|
||||
|
||||
from platformio import util
|
||||
|
||||
|
||||
def test_ping_internet_ips():
|
||||
for ip in util.PING_INTERNET_IPS:
|
||||
requests.get("http://%s" % ip, allow_redirects=False, timeout=2)
|
@ -41,4 +41,4 @@ def test_packages():
|
||||
if "X-Checksum-Sha1" not in r.headers:
|
||||
return pytest.skip("X-Checksum-Sha1 is not provided")
|
||||
|
||||
assert item['sha1'] == r.headers.get("X-Checksum-Sha1"), item
|
||||
assert item['sha1'] == r.headers.get("X-Checksum-Sha1")[0:40], item
|
||||
|
5
tox.ini
5
tox.ini
@ -20,8 +20,7 @@ basepython = python2.7
|
||||
usedevelop = True
|
||||
deps =
|
||||
isort
|
||||
flake8
|
||||
yapf<0.17
|
||||
yapf
|
||||
pylint
|
||||
pytest
|
||||
commands = python --version
|
||||
@ -47,10 +46,8 @@ commands =
|
||||
[testenv:lint]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
flake8
|
||||
pylint
|
||||
commands =
|
||||
flake8 ./platformio
|
||||
pylint --rcfile=./.pylintrc ./platformio
|
||||
|
||||
[testenv]
|
||||
|
Reference in New Issue
Block a user