forked from platformio/platformio-core
Move PIO Home to the root of source code
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,13 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,82 +0,0 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from os.path import expanduser, join
|
||||
|
||||
from platformio import __version__, app, util
|
||||
from platformio.project.helpers import get_project_core_dir, is_platformio_project
|
||||
|
||||
|
||||
class AppRPC(object):
|
||||
|
||||
APPSTATE_PATH = join(get_project_core_dir(), "homestate.json")
|
||||
|
||||
IGNORE_STORAGE_KEYS = [
|
||||
"cid",
|
||||
"coreVersion",
|
||||
"coreSystype",
|
||||
"coreCaller",
|
||||
"coreSettings",
|
||||
"homeDir",
|
||||
"projectsDir",
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def load_state():
|
||||
with app.State(AppRPC.APPSTATE_PATH, lock=True) as state:
|
||||
storage = state.get("storage", {})
|
||||
|
||||
# base data
|
||||
caller_id = app.get_session_var("caller_id")
|
||||
storage["cid"] = app.get_cid()
|
||||
storage["coreVersion"] = __version__
|
||||
storage["coreSystype"] = util.get_systype()
|
||||
storage["coreCaller"] = str(caller_id).lower() if caller_id else None
|
||||
storage["coreSettings"] = {
|
||||
name: {
|
||||
"description": data["description"],
|
||||
"default_value": data["value"],
|
||||
"value": app.get_setting(name),
|
||||
}
|
||||
for name, data in app.DEFAULT_SETTINGS.items()
|
||||
}
|
||||
|
||||
storage["homeDir"] = expanduser("~")
|
||||
storage["projectsDir"] = storage["coreSettings"]["projects_dir"]["value"]
|
||||
|
||||
# skip non-existing recent projects
|
||||
storage["recentProjects"] = [
|
||||
p for p in storage.get("recentProjects", []) if is_platformio_project(p)
|
||||
]
|
||||
|
||||
state["storage"] = storage
|
||||
state.modified = False # skip saving extra fields
|
||||
return state.as_dict()
|
||||
|
||||
@staticmethod
|
||||
def get_state():
|
||||
return AppRPC.load_state()
|
||||
|
||||
@staticmethod
|
||||
def save_state(state):
|
||||
with app.State(AppRPC.APPSTATE_PATH, lock=True) as s:
|
||||
s.clear()
|
||||
s.update(state)
|
||||
storage = s.get("storage", {})
|
||||
for k in AppRPC.IGNORE_STORAGE_KEYS:
|
||||
if k in storage:
|
||||
del storage[k]
|
||||
return True
|
||||
@@ -1,42 +0,0 @@
|
||||
# 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 time
|
||||
|
||||
import jsonrpc # pylint: disable=import-error
|
||||
from twisted.internet import defer # pylint: disable=import-error
|
||||
|
||||
|
||||
class IDERPC(object):
|
||||
def __init__(self):
|
||||
self._queue = {}
|
||||
|
||||
def send_command(self, command, params, sid=0):
|
||||
if not self._queue.get(sid):
|
||||
raise jsonrpc.exceptions.JSONRPCDispatchException(
|
||||
code=4005, message="PIO Home IDE agent is not started"
|
||||
)
|
||||
while self._queue[sid]:
|
||||
self._queue[sid].pop().callback(
|
||||
{"id": time.time(), "method": command, "params": params}
|
||||
)
|
||||
|
||||
def listen_commands(self, sid=0):
|
||||
if sid not in self._queue:
|
||||
self._queue[sid] = []
|
||||
self._queue[sid].append(defer.Deferred())
|
||||
return self._queue[sid][-1]
|
||||
|
||||
def open_project(self, project_dir, sid=0):
|
||||
return self.send_command("open_project", project_dir, sid)
|
||||
@@ -1,55 +0,0 @@
|
||||
# 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 json
|
||||
import time
|
||||
|
||||
from twisted.internet import defer, reactor # pylint: disable=import-error
|
||||
|
||||
from platformio import app
|
||||
from platformio.commands.home.rpc.handlers.os import OSRPC
|
||||
|
||||
|
||||
class MiscRPC(object):
|
||||
def load_latest_tweets(self, username):
|
||||
cache_key = "piohome_latest_tweets_" + str(username)
|
||||
cache_valid = "7d"
|
||||
with app.ContentCache() as cc:
|
||||
cache_data = cc.get(cache_key)
|
||||
if cache_data:
|
||||
cache_data = json.loads(cache_data)
|
||||
# automatically update cache in background every 12 hours
|
||||
if cache_data["time"] < (time.time() - (3600 * 12)):
|
||||
reactor.callLater(
|
||||
5, self._preload_latest_tweets, username, cache_key, cache_valid
|
||||
)
|
||||
return cache_data["result"]
|
||||
|
||||
result = self._preload_latest_tweets(username, cache_key, cache_valid)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
@defer.inlineCallbacks
|
||||
def _preload_latest_tweets(username, cache_key, cache_valid):
|
||||
result = yield OSRPC.fetch_content(
|
||||
"https://api.platformio.org/tweets/" + username
|
||||
)
|
||||
result = json.loads(result)
|
||||
with app.ContentCache() as cc:
|
||||
cc.set(
|
||||
cache_key,
|
||||
json.dumps({"time": int(time.time()), "result": result}),
|
||||
cache_valid,
|
||||
)
|
||||
defer.returnValue(result)
|
||||
@@ -1,149 +0,0 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import codecs
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
from functools import cmp_to_key
|
||||
from os.path import expanduser, isdir, isfile, join
|
||||
|
||||
import click
|
||||
from twisted.internet import defer # pylint: disable=import-error
|
||||
|
||||
from platformio import app, util
|
||||
from platformio.commands.home import helpers
|
||||
from platformio.compat import PY2, get_filesystem_encoding
|
||||
|
||||
|
||||
class OSRPC(object):
|
||||
@staticmethod
|
||||
@defer.inlineCallbacks
|
||||
def fetch_content(uri, data=None, headers=None, cache_valid=None):
|
||||
if not headers:
|
||||
headers = {
|
||||
"User-Agent": (
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) "
|
||||
"AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 "
|
||||
"Safari/603.3.8"
|
||||
)
|
||||
}
|
||||
cache_key = app.ContentCache.key_from_args(uri, data) if cache_valid else None
|
||||
with app.ContentCache() as cc:
|
||||
if cache_key:
|
||||
result = cc.get(cache_key)
|
||||
if result is not None:
|
||||
defer.returnValue(result)
|
||||
|
||||
# check internet before and resolve issue with 60 seconds timeout
|
||||
util.internet_on(raise_exception=True)
|
||||
|
||||
session = helpers.requests_session()
|
||||
if data:
|
||||
r = yield session.post(uri, data=data, headers=headers)
|
||||
else:
|
||||
r = yield session.get(uri, headers=headers)
|
||||
|
||||
r.raise_for_status()
|
||||
result = r.text
|
||||
if cache_valid:
|
||||
with app.ContentCache() as cc:
|
||||
cc.set(cache_key, result, cache_valid)
|
||||
defer.returnValue(result)
|
||||
|
||||
def request_content(self, uri, data=None, headers=None, cache_valid=None):
|
||||
if uri.startswith("http"):
|
||||
return self.fetch_content(uri, data, headers, cache_valid)
|
||||
if not isfile(uri):
|
||||
return None
|
||||
with codecs.open(uri, encoding="utf-8") as fp:
|
||||
return fp.read()
|
||||
|
||||
@staticmethod
|
||||
def open_url(url):
|
||||
return click.launch(url)
|
||||
|
||||
@staticmethod
|
||||
def reveal_file(path):
|
||||
return click.launch(
|
||||
path.encode(get_filesystem_encoding()) if PY2 else path, locate=True
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def is_file(path):
|
||||
return isfile(path)
|
||||
|
||||
@staticmethod
|
||||
def is_dir(path):
|
||||
return isdir(path)
|
||||
|
||||
@staticmethod
|
||||
def make_dirs(path):
|
||||
return os.makedirs(path)
|
||||
|
||||
@staticmethod
|
||||
def rename(src, dst):
|
||||
return os.rename(src, dst)
|
||||
|
||||
@staticmethod
|
||||
def copy(src, dst):
|
||||
return shutil.copytree(src, dst)
|
||||
|
||||
@staticmethod
|
||||
def glob(pathnames, root=None):
|
||||
if not isinstance(pathnames, list):
|
||||
pathnames = [pathnames]
|
||||
result = set()
|
||||
for pathname in pathnames:
|
||||
result |= set(glob.glob(join(root, pathname) if root else pathname))
|
||||
return list(result)
|
||||
|
||||
@staticmethod
|
||||
def list_dir(path):
|
||||
def _cmp(x, y):
|
||||
if x[1] and not y[1]:
|
||||
return -1
|
||||
if not x[1] and y[1]:
|
||||
return 1
|
||||
if x[0].lower() > y[0].lower():
|
||||
return 1
|
||||
if x[0].lower() < y[0].lower():
|
||||
return -1
|
||||
return 0
|
||||
|
||||
items = []
|
||||
if path.startswith("~"):
|
||||
path = expanduser(path)
|
||||
if not isdir(path):
|
||||
return items
|
||||
for item in os.listdir(path):
|
||||
try:
|
||||
item_is_dir = isdir(join(path, item))
|
||||
if item_is_dir:
|
||||
os.listdir(join(path, item))
|
||||
items.append((item, item_is_dir))
|
||||
except OSError:
|
||||
pass
|
||||
return sorted(items, key=cmp_to_key(_cmp))
|
||||
|
||||
@staticmethod
|
||||
def get_logical_devices():
|
||||
items = []
|
||||
for item in util.get_logical_devices():
|
||||
if item["name"]:
|
||||
item["name"] = item["name"]
|
||||
items.append(item)
|
||||
return items
|
||||
@@ -1,162 +0,0 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from io import BytesIO, StringIO
|
||||
|
||||
import click
|
||||
import jsonrpc # pylint: disable=import-error
|
||||
from twisted.internet import defer # pylint: disable=import-error
|
||||
from twisted.internet import threads # pylint: disable=import-error
|
||||
from twisted.internet import utils # pylint: disable=import-error
|
||||
|
||||
from platformio import __main__, __version__, fs
|
||||
from platformio.commands.home import helpers
|
||||
from platformio.compat import PY2, get_filesystem_encoding, is_bytes, string_types
|
||||
|
||||
try:
|
||||
from thread import get_ident as thread_get_ident
|
||||
except ImportError:
|
||||
from threading import get_ident as thread_get_ident
|
||||
|
||||
|
||||
class MultiThreadingStdStream(object):
|
||||
def __init__(self, parent_stream):
|
||||
self._buffers = {thread_get_ident(): parent_stream}
|
||||
|
||||
def __getattr__(self, name):
|
||||
thread_id = thread_get_ident()
|
||||
self._ensure_thread_buffer(thread_id)
|
||||
return getattr(self._buffers[thread_id], name)
|
||||
|
||||
def _ensure_thread_buffer(self, thread_id):
|
||||
if thread_id not in self._buffers:
|
||||
self._buffers[thread_id] = BytesIO() if PY2 else StringIO()
|
||||
|
||||
def write(self, value):
|
||||
thread_id = thread_get_ident()
|
||||
self._ensure_thread_buffer(thread_id)
|
||||
return self._buffers[thread_id].write(
|
||||
value.decode() if is_bytes(value) else value
|
||||
)
|
||||
|
||||
def get_value_and_reset(self):
|
||||
result = ""
|
||||
try:
|
||||
result = self.getvalue()
|
||||
self.truncate(0)
|
||||
self.seek(0)
|
||||
except AttributeError:
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
class PIOCoreRPC(object):
|
||||
@staticmethod
|
||||
def version():
|
||||
return __version__
|
||||
|
||||
@staticmethod
|
||||
def setup_multithreading_std_streams():
|
||||
if isinstance(sys.stdout, MultiThreadingStdStream):
|
||||
return
|
||||
PIOCoreRPC.thread_stdout = MultiThreadingStdStream(sys.stdout)
|
||||
PIOCoreRPC.thread_stderr = MultiThreadingStdStream(sys.stderr)
|
||||
sys.stdout = PIOCoreRPC.thread_stdout
|
||||
sys.stderr = PIOCoreRPC.thread_stderr
|
||||
|
||||
@staticmethod
|
||||
def call(args, options=None):
|
||||
return defer.maybeDeferred(PIOCoreRPC._call_generator, args, options)
|
||||
|
||||
@staticmethod
|
||||
@defer.inlineCallbacks
|
||||
def _call_generator(args, options=None):
|
||||
for i, arg in enumerate(args):
|
||||
if isinstance(arg, string_types):
|
||||
args[i] = arg.encode(get_filesystem_encoding()) if PY2 else arg
|
||||
else:
|
||||
args[i] = str(arg)
|
||||
|
||||
to_json = "--json-output" in args
|
||||
|
||||
try:
|
||||
if args and args[0] in ("account", "remote"):
|
||||
result = yield PIOCoreRPC._call_subprocess(args, options)
|
||||
defer.returnValue(PIOCoreRPC._process_result(result, to_json))
|
||||
else:
|
||||
result = yield PIOCoreRPC._call_inline(args, options)
|
||||
try:
|
||||
defer.returnValue(PIOCoreRPC._process_result(result, to_json))
|
||||
except ValueError:
|
||||
# fall-back to subprocess method
|
||||
result = yield PIOCoreRPC._call_subprocess(args, options)
|
||||
defer.returnValue(PIOCoreRPC._process_result(result, to_json))
|
||||
except Exception as e: # pylint: disable=bare-except
|
||||
raise jsonrpc.exceptions.JSONRPCDispatchException(
|
||||
code=4003, message="PIO Core Call Error", data=str(e)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _call_inline(args, options):
|
||||
PIOCoreRPC.setup_multithreading_std_streams()
|
||||
cwd = (options or {}).get("cwd") or os.getcwd()
|
||||
|
||||
def _thread_task():
|
||||
with fs.cd(cwd):
|
||||
exit_code = __main__.main(["-c"] + args)
|
||||
return (
|
||||
PIOCoreRPC.thread_stdout.get_value_and_reset(),
|
||||
PIOCoreRPC.thread_stderr.get_value_and_reset(),
|
||||
exit_code,
|
||||
)
|
||||
|
||||
return threads.deferToThread(_thread_task)
|
||||
|
||||
@staticmethod
|
||||
def _call_subprocess(args, options):
|
||||
cwd = (options or {}).get("cwd") or os.getcwd()
|
||||
return utils.getProcessOutputAndValue(
|
||||
helpers.get_core_fullpath(),
|
||||
args,
|
||||
path=cwd,
|
||||
env={k: v for k, v in os.environ.items() if "%" not in k},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _process_result(result, to_json=False):
|
||||
out, err, code = result
|
||||
text = ("%s\n\n%s" % (out, err)).strip()
|
||||
if code != 0:
|
||||
raise Exception(text)
|
||||
if not to_json:
|
||||
return text
|
||||
try:
|
||||
return json.loads(out)
|
||||
except ValueError as e:
|
||||
click.secho("%s => `%s`" % (e, out), fg="red", err=True)
|
||||
# if PIO Core prints unhandled warnings
|
||||
for line in out.split("\n"):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
return json.loads(line)
|
||||
except ValueError:
|
||||
pass
|
||||
raise e
|
||||
@@ -1,288 +0,0 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
from os.path import basename, expanduser, getmtime, isdir, isfile, join, realpath, sep
|
||||
|
||||
import jsonrpc # pylint: disable=import-error
|
||||
|
||||
from platformio import exception, fs
|
||||
from platformio.commands.home.rpc.handlers.app import AppRPC
|
||||
from platformio.commands.home.rpc.handlers.piocore import PIOCoreRPC
|
||||
from platformio.compat import PY2, get_filesystem_encoding
|
||||
from platformio.ide.projectgenerator import ProjectGenerator
|
||||
from platformio.managers.platform import PlatformManager
|
||||
from platformio.project.config import ProjectConfig
|
||||
from platformio.project.helpers import (
|
||||
get_project_libdeps_dir,
|
||||
get_project_src_dir,
|
||||
is_platformio_project,
|
||||
)
|
||||
|
||||
|
||||
class ProjectRPC(object):
|
||||
@staticmethod
|
||||
def _get_projects(project_dirs=None):
|
||||
def _get_project_data(project_dir):
|
||||
data = {"boards": [], "envLibdepsDirs": [], "libExtraDirs": []}
|
||||
config = ProjectConfig(join(project_dir, "platformio.ini"))
|
||||
libdeps_dir = get_project_libdeps_dir()
|
||||
|
||||
data["libExtraDirs"].extend(config.get("platformio", "lib_extra_dirs", []))
|
||||
|
||||
for section in config.sections():
|
||||
if not section.startswith("env:"):
|
||||
continue
|
||||
data["envLibdepsDirs"].append(join(libdeps_dir, section[4:]))
|
||||
if config.has_option(section, "board"):
|
||||
data["boards"].append(config.get(section, "board"))
|
||||
data["libExtraDirs"].extend(config.get(section, "lib_extra_dirs", []))
|
||||
|
||||
# skip non existing folders and resolve full path
|
||||
for key in ("envLibdepsDirs", "libExtraDirs"):
|
||||
data[key] = [
|
||||
expanduser(d) if d.startswith("~") else realpath(d)
|
||||
for d in data[key]
|
||||
if isdir(d)
|
||||
]
|
||||
|
||||
return data
|
||||
|
||||
def _path_to_name(path):
|
||||
return (sep).join(path.split(sep)[-2:])
|
||||
|
||||
if not project_dirs:
|
||||
project_dirs = AppRPC.load_state()["storage"]["recentProjects"]
|
||||
|
||||
result = []
|
||||
pm = PlatformManager()
|
||||
for project_dir in project_dirs:
|
||||
data = {}
|
||||
boards = []
|
||||
try:
|
||||
with fs.cd(project_dir):
|
||||
data = _get_project_data(project_dir)
|
||||
except exception.PlatformIOProjectException:
|
||||
continue
|
||||
|
||||
for board_id in data.get("boards", []):
|
||||
name = board_id
|
||||
try:
|
||||
name = pm.board_config(board_id)["name"]
|
||||
except exception.PlatformioException:
|
||||
pass
|
||||
boards.append({"id": board_id, "name": name})
|
||||
|
||||
result.append(
|
||||
{
|
||||
"path": project_dir,
|
||||
"name": _path_to_name(project_dir),
|
||||
"modified": int(getmtime(project_dir)),
|
||||
"boards": boards,
|
||||
"envLibStorages": [
|
||||
{"name": basename(d), "path": d}
|
||||
for d in data.get("envLibdepsDirs", [])
|
||||
],
|
||||
"extraLibStorages": [
|
||||
{"name": _path_to_name(d), "path": d}
|
||||
for d in data.get("libExtraDirs", [])
|
||||
],
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
def get_projects(self, project_dirs=None):
|
||||
return self._get_projects(project_dirs)
|
||||
|
||||
@staticmethod
|
||||
def get_project_examples():
|
||||
result = []
|
||||
for manifest in PlatformManager().get_installed():
|
||||
examples_dir = join(manifest["__pkg_dir"], "examples")
|
||||
if not isdir(examples_dir):
|
||||
continue
|
||||
items = []
|
||||
for project_dir, _, __ in os.walk(examples_dir):
|
||||
project_description = None
|
||||
try:
|
||||
config = ProjectConfig(join(project_dir, "platformio.ini"))
|
||||
config.validate(silent=True)
|
||||
project_description = config.get("platformio", "description")
|
||||
except exception.PlatformIOProjectException:
|
||||
continue
|
||||
|
||||
path_tokens = project_dir.split(sep)
|
||||
items.append(
|
||||
{
|
||||
"name": "/".join(
|
||||
path_tokens[path_tokens.index("examples") + 1 :]
|
||||
),
|
||||
"path": project_dir,
|
||||
"description": project_description,
|
||||
}
|
||||
)
|
||||
result.append(
|
||||
{
|
||||
"platform": {
|
||||
"title": manifest["title"],
|
||||
"version": manifest["version"],
|
||||
},
|
||||
"items": sorted(items, key=lambda item: item["name"]),
|
||||
}
|
||||
)
|
||||
return sorted(result, key=lambda data: data["platform"]["title"])
|
||||
|
||||
def init(self, board, framework, project_dir):
|
||||
assert project_dir
|
||||
state = AppRPC.load_state()
|
||||
if not isdir(project_dir):
|
||||
os.makedirs(project_dir)
|
||||
args = ["init", "--board", board]
|
||||
if framework:
|
||||
args.extend(["--project-option", "framework = %s" % framework])
|
||||
if (
|
||||
state["storage"]["coreCaller"]
|
||||
and state["storage"]["coreCaller"] in ProjectGenerator.get_supported_ides()
|
||||
):
|
||||
args.extend(["--ide", state["storage"]["coreCaller"]])
|
||||
d = PIOCoreRPC.call(args, options={"cwd": project_dir})
|
||||
d.addCallback(self._generate_project_main, project_dir, framework)
|
||||
return d
|
||||
|
||||
@staticmethod
|
||||
def _generate_project_main(_, project_dir, framework):
|
||||
main_content = None
|
||||
if framework == "arduino":
|
||||
main_content = "\n".join(
|
||||
[
|
||||
"#include <Arduino.h>",
|
||||
"",
|
||||
"void setup() {",
|
||||
" // put your setup code here, to run once:",
|
||||
"}",
|
||||
"",
|
||||
"void loop() {",
|
||||
" // put your main code here, to run repeatedly:",
|
||||
"}" "",
|
||||
]
|
||||
) # yapf: disable
|
||||
elif framework == "mbed":
|
||||
main_content = "\n".join(
|
||||
[
|
||||
"#include <mbed.h>",
|
||||
"",
|
||||
"int main() {",
|
||||
"",
|
||||
" // put your setup code here, to run once:",
|
||||
"",
|
||||
" while(1) {",
|
||||
" // put your main code here, to run repeatedly:",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
]
|
||||
) # yapf: disable
|
||||
if not main_content:
|
||||
return project_dir
|
||||
with fs.cd(project_dir):
|
||||
src_dir = get_project_src_dir()
|
||||
main_path = join(src_dir, "main.cpp")
|
||||
if isfile(main_path):
|
||||
return project_dir
|
||||
if not isdir(src_dir):
|
||||
os.makedirs(src_dir)
|
||||
with open(main_path, "w") as f:
|
||||
f.write(main_content.strip())
|
||||
return project_dir
|
||||
|
||||
def import_arduino(self, board, use_arduino_libs, arduino_project_dir):
|
||||
board = str(board)
|
||||
if arduino_project_dir and PY2:
|
||||
arduino_project_dir = arduino_project_dir.encode(get_filesystem_encoding())
|
||||
# don't import PIO Project
|
||||
if is_platformio_project(arduino_project_dir):
|
||||
return arduino_project_dir
|
||||
|
||||
is_arduino_project = any(
|
||||
[
|
||||
isfile(
|
||||
join(
|
||||
arduino_project_dir,
|
||||
"%s.%s" % (basename(arduino_project_dir), ext),
|
||||
)
|
||||
)
|
||||
for ext in ("ino", "pde")
|
||||
]
|
||||
)
|
||||
if not is_arduino_project:
|
||||
raise jsonrpc.exceptions.JSONRPCDispatchException(
|
||||
code=4000, message="Not an Arduino project: %s" % arduino_project_dir
|
||||
)
|
||||
|
||||
state = AppRPC.load_state()
|
||||
project_dir = join(
|
||||
state["storage"]["projectsDir"], time.strftime("%y%m%d-%H%M%S-") + board
|
||||
)
|
||||
if not isdir(project_dir):
|
||||
os.makedirs(project_dir)
|
||||
args = ["init", "--board", board]
|
||||
args.extend(["--project-option", "framework = arduino"])
|
||||
if use_arduino_libs:
|
||||
args.extend(
|
||||
["--project-option", "lib_extra_dirs = ~/Documents/Arduino/libraries"]
|
||||
)
|
||||
if (
|
||||
state["storage"]["coreCaller"]
|
||||
and state["storage"]["coreCaller"] in ProjectGenerator.get_supported_ides()
|
||||
):
|
||||
args.extend(["--ide", state["storage"]["coreCaller"]])
|
||||
d = PIOCoreRPC.call(args, options={"cwd": project_dir})
|
||||
d.addCallback(self._finalize_arduino_import, project_dir, arduino_project_dir)
|
||||
return d
|
||||
|
||||
@staticmethod
|
||||
def _finalize_arduino_import(_, project_dir, arduino_project_dir):
|
||||
with fs.cd(project_dir):
|
||||
src_dir = get_project_src_dir()
|
||||
if isdir(src_dir):
|
||||
fs.rmtree(src_dir)
|
||||
shutil.copytree(arduino_project_dir, src_dir)
|
||||
return project_dir
|
||||
|
||||
@staticmethod
|
||||
def import_pio(project_dir):
|
||||
if not project_dir or not is_platformio_project(project_dir):
|
||||
raise jsonrpc.exceptions.JSONRPCDispatchException(
|
||||
code=4001, message="Not an PlatformIO project: %s" % project_dir
|
||||
)
|
||||
new_project_dir = join(
|
||||
AppRPC.load_state()["storage"]["projectsDir"],
|
||||
time.strftime("%y%m%d-%H%M%S-") + basename(project_dir),
|
||||
)
|
||||
shutil.copytree(project_dir, new_project_dir)
|
||||
|
||||
state = AppRPC.load_state()
|
||||
args = ["init"]
|
||||
if (
|
||||
state["storage"]["coreCaller"]
|
||||
and state["storage"]["coreCaller"] in ProjectGenerator.get_supported_ides()
|
||||
):
|
||||
args.extend(["--ide", state["storage"]["coreCaller"]])
|
||||
d = PIOCoreRPC.call(args, options={"cwd": new_project_dir})
|
||||
d.addCallback(lambda _: new_project_dir)
|
||||
return d
|
||||
@@ -1,75 +0,0 @@
|
||||
# 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.
|
||||
|
||||
# pylint: disable=import-error
|
||||
|
||||
import click
|
||||
import jsonrpc
|
||||
from autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol
|
||||
from jsonrpc.exceptions import JSONRPCDispatchException
|
||||
from twisted.internet import defer
|
||||
|
||||
from platformio.compat import PY2, dump_json_to_unicode, is_bytes
|
||||
|
||||
|
||||
class JSONRPCServerProtocol(WebSocketServerProtocol):
|
||||
def onMessage(self, payload, isBinary): # pylint: disable=unused-argument
|
||||
# click.echo("> %s" % payload)
|
||||
response = jsonrpc.JSONRPCResponseManager.handle(
|
||||
payload, self.factory.dispatcher
|
||||
).data
|
||||
# if error
|
||||
if "result" not in response:
|
||||
self.sendJSONResponse(response)
|
||||
return None
|
||||
|
||||
d = defer.maybeDeferred(lambda: response["result"])
|
||||
d.addCallback(self._callback, response)
|
||||
d.addErrback(self._errback, response)
|
||||
|
||||
return None
|
||||
|
||||
def _callback(self, result, response):
|
||||
response["result"] = result
|
||||
self.sendJSONResponse(response)
|
||||
|
||||
def _errback(self, failure, response):
|
||||
if isinstance(failure.value, JSONRPCDispatchException):
|
||||
e = failure.value
|
||||
else:
|
||||
e = JSONRPCDispatchException(code=4999, message=failure.getErrorMessage())
|
||||
del response["result"]
|
||||
response["error"] = e.error._data # pylint: disable=protected-access
|
||||
self.sendJSONResponse(response)
|
||||
|
||||
def sendJSONResponse(self, response):
|
||||
# click.echo("< %s" % response)
|
||||
if "error" in response:
|
||||
click.secho("Error: %s" % response["error"], fg="red", err=True)
|
||||
response = dump_json_to_unicode(response)
|
||||
if not PY2 and not is_bytes(response):
|
||||
response = response.encode("utf-8")
|
||||
self.sendMessage(response)
|
||||
|
||||
|
||||
class JSONRPCServerFactory(WebSocketServerFactory):
|
||||
|
||||
protocol = JSONRPCServerProtocol
|
||||
|
||||
def __init__(self):
|
||||
super(JSONRPCServerFactory, self).__init__()
|
||||
self.dispatcher = jsonrpc.Dispatcher()
|
||||
|
||||
def addHandler(self, handler, namespace):
|
||||
self.dispatcher.build_method_map(handler, prefix="%s." % namespace)
|
||||
Reference in New Issue
Block a user