diff --git a/platformio/__init__.py b/platformio/__init__.py index 695d579e..20a96f5b 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (0, 7, "0-dev") +VERSION = (0, 7, "0.dev") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" @@ -14,4 +14,6 @@ __email__ = "me@ikravets.com" __license__ = "MIT License" __copyright__ = "Copyright (C) 2014 Ivan Kravets" +# __apiurl__ = "http://127.0.0.1:8080" +__apiurl__ = "http://api.platformio.ikravets.com" __pkgmanifesturl__ = "http://platformio.ikravets.com/packages/manifest.json" diff --git a/platformio/builder/main.py b/platformio/builder/main.py index 97c0be78..07fd7725 100644 --- a/platformio/builder/main.py +++ b/platformio/builder/main.py @@ -16,7 +16,8 @@ from os.path import isdir, join from SCons.Script import (DefaultEnvironment, Exit, SConscript, SConscriptChdir, Variables) -from platformio.util import get_pioenvs_dir, get_project_dir, get_source_dir +from platformio.util import (get_lib_dir, get_pioenvs_dir, get_project_dir, + get_source_dir) # AllowSubstExceptions() @@ -57,6 +58,7 @@ DefaultEnvironment( BUILD_DIR=join("$PIOENVS_DIR", "$PIOENV"), LIBSOURCE_DIRS=[ join("$PROJECT_DIR", "lib"), + get_lib_dir(), join("$PLATFORMFW_DIR", "libraries"), ] ) diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index 103ca4b5..5285309f 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -165,8 +165,9 @@ def ConvertInotoCpp(env): remove(f) tmpcpp = [] - - for item in env.Glob(join("$PROJECT_DIR", "src", "*.ino")): + items = (env.Glob(join("$PROJECT_DIR", "src", "*.ino")) + + env.Glob(join("$PROJECT_DIR", "src", "*.pde"))) + for item in items: cppfile = item.get_path()[:-3] + "cpp" if isfile(cppfile): continue diff --git a/platformio/commands/lib.py b/platformio/commands/lib.py new file mode 100644 index 00000000..fc5f1bcc --- /dev/null +++ b/platformio/commands/lib.py @@ -0,0 +1,124 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from urllib import quote + +from click import argument, echo, group, option, secho, style + +from platformio.exception import LibAlreadyInstalledError +from platformio.libmanager import LibraryManager +from platformio.util import get_api_result, get_lib_dir + + +@group(short_help="Library Manager") +def cli(): + pass + + +@cli.command("search", short_help="Search for library") +@argument("query") +def lib_search(query): + result = get_api_result("/lib/search", dict(query=quote(query))) + secho("Found %d libraries:" % result['total'], + fg="green" if result['total'] else "yellow") + for item in result['items']: + echo("{name:<30} {description}".format( + name=style(item['name'], fg="cyan"), + description=item['description'] + )) + + +@cli.command("install", short_help="Install library") +@argument("names", nargs=-1) +@option("-v", "--version") +def lib_install_cli(names, version): + lib_install(names, version) + + +def lib_install(names, version=None): + lm = LibraryManager(get_lib_dir()) + for name in names: + echo("Installing %s library:" % style(name, fg="cyan")) + try: + if lm.install(name, version): + secho("The library '%s' has been successfully installed!" % + name, fg="green") + except LibAlreadyInstalledError: + secho("Already installed", fg="yellow") + + +@cli.command("uninstall", short_help="Uninstall libraries") +@argument("names", nargs=-1) +def lib_uninstall_cli(names): + lib_uninstall(names) + + +def lib_uninstall(names): + lm = LibraryManager(get_lib_dir()) + for name in names: + if lm.uninstall(name): + secho("The library '%s' has been successfully " + "uninstalled!" % name, fg="green") + + +@cli.command("list", short_help="List installed libraries") +def lib_list(): + lm = LibraryManager(get_lib_dir()) + for name in lm.get_installed(): + info = lm.get_info(name) + echo("{name:<30} {description}".format( + name=style(info['name'], fg="cyan"), + description=info['description'] + )) + + +@cli.command("show", short_help="Show details about installed libraries") +@argument("name") +def lib_show(name): + lm = LibraryManager(get_lib_dir()) + info = lm.get_info(name) + secho(info['name'], fg="cyan") + echo("-" * len(info['name'])) + + if "author" in info: + _data = [] + for k in ("name", "email"): + if k in info['author'] and info['author'][k] is not None: + _value = info['author'][k] + if k == "email": + _value = "<%s>" % _value + _data.append(_value) + echo("Author: %s" % " ".join(_data)) + + echo("Keywords: %s" % info['keywords']) + echo("Version: %s" % info['version']) + echo() + echo(info['description']) + echo() + + +@cli.command("update", short_help="Update installed libraries") +def lib_update(): + lm = LibraryManager(get_lib_dir()) + lib_names = lm.get_installed() + versions = get_api_result("/lib/version/" + ",".join(lib_names)) + + for name in lib_names: + info = lm.get_info(name) + + echo("Updating %s library:" % style(name, fg="yellow")) + + current_version = info['version'] + latest_version = versions[name] + + echo("Versions: Current=%s, Latest=%s \t " % ( + current_version, latest_version), nl=False) + + if current_version == latest_version: + echo("[%s]" % (style("Up-to-date", fg="green"))) + continue + else: + echo("[%s]" % (style("Out-of-date", fg="red"))) + + lib_uninstall([name]) + lib_install([name]) diff --git a/platformio/commands/show.py b/platformio/commands/show.py index 616a7a51..38cb4212 100644 --- a/platformio/commands/show.py +++ b/platformio/commands/show.py @@ -10,7 +10,7 @@ from platformio.pkgmanager import PackageManager from platformio.platforms.base import PlatformFactory -@command("show", short_help="Show details about an installed platforms") +@command("show", short_help="Show details about installed platforms") @argument("platform") def cli(platform): p = PlatformFactory().newPlatform(platform) diff --git a/platformio/exception.py b/platformio/exception.py index 046ad084..920ee521 100644 --- a/platformio/exception.py +++ b/platformio/exception.py @@ -105,3 +105,17 @@ class GetSerialPortsError(PlatformioException): class GetLatestVersionError(PlatformioException): MESSAGE = "Can't retrieve latest PlatformIO version" + + +class APIRequestError(PlatformioException): + + MESSAGE = "[API] %s" + + +class LibAlreadyInstalledError(PlatformioException): + pass + + +class LibNotInstalledError(PlatformioException): + + MESSAGE = "Library '%s' has not been installed yet" diff --git a/platformio/libmanager.py b/platformio/libmanager.py new file mode 100644 index 00000000..2fb4a185 --- /dev/null +++ b/platformio/libmanager.py @@ -0,0 +1,75 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +import json +from os import listdir, makedirs, remove +from os.path import isdir, isfile, join +from shutil import rmtree +from tempfile import gettempdir + +from platformio.downloader import FileDownloader +from platformio.exception import LibAlreadyInstalledError, LibNotInstalledError +from platformio.unpacker import FileUnpacker +from platformio.util import get_api_result + + +class LibraryManager(object): + + CONFIG_NAME = "library.json" + + def __init__(self, lib_dir): + self.lib_dir = lib_dir + + @staticmethod + def download(url, dest_dir): + fd = FileDownloader(url, dest_dir) + fd.start() + return fd.get_filepath() + + @staticmethod + def unpack(pkgpath, dest_dir): + fu = FileUnpacker(pkgpath, dest_dir) + return fu.start() + + def get_installed(self): + items = [] + for item in listdir(self.lib_dir): + conf_path = join(self.lib_dir, item, self.CONFIG_NAME) + if isfile(conf_path): + items.append(item) + return items + + def get_info(self, name): + conf_path = join(self.lib_dir, name, self.CONFIG_NAME) + if not isfile(conf_path): + raise LibNotInstalledError(name) + with open(conf_path, "r") as f: + return json.load(f) + + def is_installed(self, name): + return isfile(join(self.lib_dir, name, self.CONFIG_NAME)) + + def install(self, name, version=None): + if self.is_installed(name): + raise LibAlreadyInstalledError() + + _lib_dir = join(self.lib_dir, name) + if not isdir(_lib_dir): + makedirs(_lib_dir) + + dlinfo = get_api_result("/lib/download/" + name, dict(version=version) + if version else None) + try: + dlpath = self.download(dlinfo['url'], gettempdir()) + self.unpack(dlpath, _lib_dir) + finally: + remove(dlpath) + + return self.is_installed(name) + + def uninstall(self, name): + if self.is_installed(name): + rmtree(join(self.lib_dir, name)) + return True + else: + raise LibNotInstalledError(name) diff --git a/platformio/util.py b/platformio/util.py index 34a1d785..63a10475 100644 --- a/platformio/util.py +++ b/platformio/util.py @@ -8,9 +8,14 @@ from platform import system, uname from subprocess import PIPE, Popen from time import sleep +from requests import get +from requests.exceptions import ConnectionError, HTTPError +from requests.utils import default_user_agent from serial import Serial -from platformio.exception import GetSerialPortsError, NotPlatformProject +from platformio import __apiurl__, __version__ +from platformio.exception import (APIRequestError, GetSerialPortsError, + NotPlatformProject) try: from configparser import ConfigParser @@ -36,6 +41,17 @@ def get_home_dir(): return expanduser("~/.platformio") +def get_lib_dir(): + try: + config = get_project_config() + if (config.has_section("platformio") and + config.has_option("platformio", "lib_dir")): + return config.get("platformio", "lib_dir") + except NotPlatformProject: + pass + return join(get_home_dir(), "lib") + + def get_source_dir(): return dirname(realpath(__file__)) @@ -96,3 +112,27 @@ def get_serialports(): else: raise GetSerialPortsError(os_name) return[{"port": p, "description": d, "hwid": h} for p, d, h in comports()] + + +def get_api_result(path, params=None): + result = None + r = None + try: + headers = {"User-Agent": "PlatformIO/%s %s" % ( + __version__, default_user_agent())} + r = get(__apiurl__ + path, params=params, headers=headers) + result = r.json() + r.raise_for_status() + except HTTPError as e: + if result and "errors" in result: + raise APIRequestError(result['errors'][0]['title']) + else: + raise APIRequestError(e) + except ConnectionError: + raise APIRequestError("Could not connect to PlatformIO API Service") + except ValueError: + raise APIRequestError("Invalid response: %s" % r.text) + finally: + if r: + r.close() + return result