PlatformIO Library Manager 3.0 // Resolve #588 , Resolve #414, Resolve #498, Resolve #475, Resolve #410, Resolve #461, Resolve #361, Resolve #507, Resolve #554

This commit is contained in:
Ivan Kravets committed 2016-08-02 19:10:29 +03:00
1 parent 5b5a63cb5f
commit 758396c9ea
34 files changed
+1185 -700

No files matched your search

+1 -1
View File
@@ -14,7 +14,7 @@
import sys
VERSION = (3, 0, "0.dev15")
VERSION = (3, 0, "0.dev16")
__version__ = ".".join([str(s) for s in VERSION])
__title__ = "platformio"
+164 -169
View File
@@ -1,4 +1,4 @@
# Copyright 2014-2016 Ivan Kravets <me@ikravets.com>
# Copyright 2014-present Ivan Kravets <me@ikravets.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,51 +13,136 @@
# limitations under the License.
import json
from os.path import join
import click
from platformio import app, exception
from platformio.libmanager import LibraryManager
from platformio import app, exception, util
from platformio.managers.lib import LibraryManager
from platformio.util import get_api_result
@click.group(short_help="Library Manager")
@click.option(
"-g",
"--global",
is_flag=True,
help="Manager global PlatformIO"
" library storage `%s`" % join(util.get_home_dir(), "lib"))
@click.option(
"-d",
"--storage-dir",
default=None,
type=click.Path(
exists=True,
file_okay=False,
dir_okay=True,
writable=True,
resolve_path=True),
help="Manage custom library storage")
@click.pass_context
def cli(ctx, **options):
# skip commands that don't need storage folder
if ctx.invoked_subcommand in ("search", "register") or \
(len(ctx.args) == 2 and ctx.args[1] in ("-h", "--help")):
return
storage_dir = options['storage_dir']
if not storage_dir and options['global']:
storage_dir = join(util.get_home_dir(), "lib")
if not storage_dir and not util.is_platformio_project():
raise exception.PlatformioException(
"The `%s` is not a PlatformIO project.\nTo manage libraries "
"in the global storage `%s`, please use "
"`platformio lib --global %s` instead." %
(util.get_project_dir(), join(util.get_home_dir(), "lib"),
ctx.invoked_subcommand))
ctx.obj = LibraryManager(storage_dir)
click.echo("Library Storage: " + storage_dir)
@cli.command("install", short_help="Install library")
@click.argument("libraries", required=False, nargs=-1, metavar="[LIBRARY...]")
@click.option(
"--save",
is_flag=True,
help="Save installed libraries into the project's platformio.ini "
"library dependencies")
@click.option(
"-q", "--quiet", is_flag=True, help="Suppress progress reporting")
@click.pass_obj
def lib_install(lm, libraries, save, quiet): # pylint: disable=unused-argument
# @TODO "save" option
for library in libraries:
lm.install(library, quiet=quiet)
@cli.command("uninstall", short_help="Uninstall libraries")
@click.argument("libraries", nargs=-1, metavar="[LIBRARY...]")
@click.pass_obj
def lib_uninstall(lm, libraries):
for library in libraries:
lm.uninstall(library)
@cli.command("update", short_help="Update installed libraries")
@click.argument("libraries", required=False, nargs=-1, metavar="[LIBRARY...]")
@click.option(
"-c",
"--only-check",
is_flag=True,
help="Do not update, only check for new version")
@click.pass_obj
def lib_update(lm, libraries, only_check):
if not libraries:
libraries = [str(m.get("id", m['name'])) for m in lm.get_installed()]
for library in libraries:
lm.update(library, only_check=only_check)
#######
LIBLIST_TPL = ("[{id:^14}] {name:<25} {compatibility:<30} "
"\"{authornames}\": {description}")
def echo_liblist_header():
click.echo(LIBLIST_TPL.format(
id=click.style("ID", fg="green"),
name=click.style("Name", fg="cyan"),
compatibility=click.style("Compatibility", fg="yellow"),
authornames="Authors",
description="Description"
))
click.echo(
LIBLIST_TPL.format(
id=click.style(
"ID", fg="green"),
name=click.style(
"Name", fg="cyan"),
compatibility=click.style(
"Compatibility", fg="yellow"),
authornames="Authors",
description="Description"))
terminal_width, _ = click.get_terminal_size()
click.echo("-" * terminal_width)
def echo_liblist_item(item):
click.echo(LIBLIST_TPL.format(
id=click.style(str(item['id']), fg="green"),
name=click.style(item['name'], fg="cyan"),
compatibility=click.style(
", ".join(item['frameworks'] + item['platforms']),
fg="yellow"
),
authornames=", ".join(item['authornames']),
description=item['description']
))
@click.group(short_help="Library Manager")
def cli():
pass
click.echo(
LIBLIST_TPL.format(
id=click.style(
str(item.get("id", "VCS")), fg="green"),
name=click.style(
item['name'], fg="cyan"),
compatibility=click.style(
", ".join(
item.get("frameworks", ["-"]) + item.get("platforms", [])),
fg="yellow"),
authornames=", ".join(item.get("authornames", ["Unknown"])),
description=item.get("description", item.get("url", ""))) +
(" | @" + click.style(
item['version'], fg="yellow") if "version" in item else ""))
@cli.command("search", short_help="Search for library")
@click.option("--json-output", is_flag=True)
@click.option("--page", type=click.INT, default=1)
@click.option("-n", "--name", multiple=True)
@click.option("-a", "--author", multiple=True)
@click.option("-k", "--keyword", multiple=True)
@click.option("-f", "--framework", multiple=True)
@@ -73,8 +158,9 @@ def lib_search(query, json_output, page, **filters):
for value in values:
query.append('%s:"%s"' % (key, value))
result = get_api_result("/lib/search",
dict(query=" ".join(query), page=page))
result = get_api_result(
"/lib/search", dict(
query=" ".join(query), page=page))
if json_output:
click.echo(json.dumps(result))
@@ -84,17 +170,22 @@ def lib_search(query, json_output, page, **filters):
click.secho(
"Nothing has been found by your request\n"
"Try a less-specific search or use truncation (or wildcard) "
"operator", fg="yellow", nl=False)
"operator",
fg="yellow",
nl=False)
click.secho(" *", fg="green")
click.secho("For example: DS*, PCA*, DHT* and etc.\n", fg="yellow")
click.echo("For more examples and advanced search syntax, "
"please use documentation:")
click.secho("http://docs.platformio.org"
"/en/latest/userguide/lib/cmd_search.html\n", fg="cyan")
click.secho(
"http://docs.platformio.org"
"/en/latest/userguide/lib/cmd_search.html\n",
fg="cyan")
return
click.secho("Found %d libraries:\n" % result['total'],
fg="green" if result['total'] else "yellow")
click.secho(
"Found %d libraries:\n" % result['total'],
fg="green" if result['total'] else "yellow")
if result['total']:
echo_liblist_header()
@@ -111,94 +202,17 @@ def lib_search(query, json_output, page, **filters):
click.confirm("Show next libraries?")):
result = get_api_result(
"/lib/search",
dict(query=" ".join(query), page=int(result['page']) + 1)
)
dict(
query=" ".join(query), page=int(result['page']) + 1))
else:
break
@cli.command("install", short_help="Install library")
@click.argument("libid", type=click.INT, nargs=-1, metavar="[LIBRARY_ID]")
@click.option("-v", "--version")
@click.pass_context
def lib_install(ctx, libid, version):
lm = LibraryManager()
for id_ in libid:
click.echo(
"Installing library [ %s ]:" % click.style(str(id_), fg="green"))
try:
if not lm.install(id_, version):
continue
info = lm.get_info(id_)
click.secho(
"The library #%s '%s' has been successfully installed!"
% (str(id_), info['name']), fg="green")
if "dependencies" in info:
click.secho("Installing dependencies:", fg="yellow")
_dependencies = info['dependencies']
if not isinstance(_dependencies, list):
_dependencies = [_dependencies]
for item in _dependencies:
try:
lib_install_dependency(ctx, item)
except AssertionError:
raise exception.LibInstallDependencyError(str(item))
except exception.LibAlreadyInstalled:
click.secho("Already installed", fg="yellow")
def lib_install_dependency(ctx, data):
assert isinstance(data, dict)
query = []
for key in data:
if key in ("authors", "frameworks", "platforms", "keywords"):
values = data[key]
if not isinstance(values, list):
values = [v.strip() for v in values.split(",") if v]
for value in values:
query.append('%s:"%s"' % (key[:-1], value))
elif isinstance(data[key], basestring):
query.append('+"%s"' % data[key])
result = get_api_result("/lib/search", dict(query=" ".join(query)))
assert result['total'] > 0
if result['total'] == 1 or not app.get_setting("enable_prompts"):
ctx.invoke(lib_install, libid=[result['items'][0]['id']])
else:
click.secho(
"Conflict: More than one dependent libraries have been found "
"by request %s:" % json.dumps(data), fg="red")
echo_liblist_header()
for item in result['items']:
echo_liblist_item(item)
deplib_id = click.prompt(
"Please choose one dependent library ID",
type=click.Choice([str(i['id']) for i in result['items']]))
ctx.invoke(lib_install, libid=[int(deplib_id)])
@cli.command("uninstall", short_help="Uninstall libraries")
@click.argument("libid", type=click.INT, nargs=-1)
def lib_uninstall(libid):
lm = LibraryManager()
for id_ in libid:
info = lm.get_info(id_)
if lm.uninstall(id_):
click.secho("The library #%s '%s' has been successfully "
"uninstalled!" % (str(id_), info['name']), fg="green")
@cli.command("list", short_help="List installed libraries")
@click.option("--json-output", is_flag=True)
def lib_list(json_output):
lm = LibraryManager()
items = lm.get_installed().values()
@click.pass_obj
def lib_list(lm, json_output):
items = lm.get_installed()
if json_output:
click.echo(json.dumps(items))
@@ -208,21 +222,34 @@ def lib_list(json_output):
return
echo_liblist_header()
for item in sorted(items, key=lambda i: i['id']):
item['authornames'] = [i['name'] for i in item['authors']]
for item in sorted(items, key=lambda i: i['name']):
if "authors" in item:
item['authornames'] = [i['name'] for i in item['authors']]
echo_liblist_item(item)
@cli.command("show", short_help="Show details about installed library")
@click.argument("libid", type=click.INT)
def lib_show(libid):
lm = LibraryManager()
info = lm.get_info(libid)
click.secho(info['name'], fg="cyan")
click.echo("-" * len(info['name']))
@click.pass_obj
@click.argument("library", metavar="[LIBRARY]")
def lib_show(lm, library): # pylint: disable=too-many-branches
name, requirements, url = lm.parse_pkg_name(library)
installed_dir = lm.get_installed_dir(name, requirements, url)
if not installed_dir:
click.secho(
"%s @ %s is not installed" % (name, requirements or "*"),
fg="yellow")
return
manifest = lm.load_manifest(installed_dir)
click.secho(manifest['name'], fg="cyan")
click.echo("=" * len(manifest['name']))
if "description" in manifest:
click.echo(manifest['description'])
click.echo()
_authors = []
for author in info['authors']:
for author in manifest.get("authors", []):
_data = []
for key in ("name", "email", "url", "maintainer"):
if not author[key]:
@@ -234,61 +261,29 @@ def lib_show(libid):
else:
_data.append(author[key])
_authors.append(" ".join(_data))
click.echo("Authors: %s" % ", ".join(_authors))
if _authors:
click.echo("Authors: %s" % ", ".join(_authors))
click.echo("Keywords: %s" % ", ".join(info['keywords']))
if "frameworks" in info:
click.echo("Frameworks: %s" % ", ".join(info['frameworks']))
if "platforms" in info:
click.echo("Platforms: %s" % ", ".join(info['platforms']))
click.echo("Version: %s" % info['version'])
click.echo()
click.echo(info['description'])
click.echo()
@cli.command("update", short_help="Update installed libraries")
@click.argument("libid", type=click.INT, nargs=-1, required=False,
metavar="[LIBRARY_ID]")
@click.pass_context
def lib_update(ctx, libid):
lm = LibraryManager()
for id_, latest_version in (lm.get_latest_versions() or {}).items():
if libid and int(id_) not in libid:
continue
info = lm.get_info(int(id_))
click.echo("Updating [ %s ] %s library:" % (
click.style(id_, fg="yellow"),
click.style(info['name'], fg="cyan")))
current_version = info['version']
if latest_version is None:
click.secho("Unknown library", fg="red")
continue
click.echo("Versions: Current=%s, Latest=%s \t " % (
current_version, latest_version), nl=False)
if current_version == latest_version:
click.echo("[%s]" % (click.style("Up-to-date", fg="green")))
for key in ("keywords", "frameworks", "platforms", "license", "url",
"version"):
if key not in manifest:
continue
if isinstance(manifest[key], list):
click.echo("%s: %s" % (key.title(), ", ".join(manifest[key])))
else:
click.echo("[%s]" % (click.style("Out-of-date", fg="red")))
ctx.invoke(lib_uninstall, libid=[int(id_)])
ctx.invoke(lib_install, libid=[int(id_)])
click.echo("%s: %s" % (key.title(), manifest[key]))
@cli.command("register", short_help="Register new library")
@click.argument("config_url")
def lib_register(config_url):
if (not config_url.startswith("http://") and not
config_url.startswith("https://")):
if (not config_url.startswith("http://") and
not config_url.startswith("https://")):
raise exception.InvalidLibConfURL(config_url)
result = get_api_result("/lib/register", data=dict(config_url=config_url))
if "message" in result and result['message']:
click.secho(result['message'], fg="green" if "successed" in result and
result['successed'] else "red")
click.secho(
result['message'],
fg="green"
if "successed" in result and result['successed'] else "red")
+13 -20
View File
@@ -21,11 +21,11 @@ from time import time
import click
from platformio import __version__, app, exception, telemetry, util
from platformio import __version__, exception, telemetry, util
from platformio.commands.lib import lib_install as cmd_lib_install
from platformio.commands.platform import \
platform_install as cmd_platform_install
from platformio.libmanager import LibraryManager
from platformio.managers.lib import LibraryManager
from platformio.managers.platform import PlatformFactory
@@ -188,8 +188,8 @@ class EnvironmentProcessor(object):
# install dependent libraries
if "lib_install" in self.options:
_autoinstall_libs(self.cmd_ctx, self.options['lib_install'])
_autoinstall_libs(self.cmd_ctx, self.options['lib_install'],
self.verbose)
try:
p = PlatformFactory.newPlatform(self.options['platform'])
@@ -201,22 +201,15 @@ class EnvironmentProcessor(object):
return p.run(build_vars, build_targets, self.verbose)
def _autoinstall_libs(ctx, libids_list):
require_libs = [int(l.strip()) for l in libids_list.split(",")]
installed_libs = [
l['id'] for l in LibraryManager().get_installed().values()
]
not_intalled_libs = set(require_libs) - set(installed_libs)
if not require_libs or not not_intalled_libs:
return
if (not app.get_setting("enable_prompts") or
click.confirm(
"The libraries with IDs '%s' have not been installed yet. "
"Would you like to install them now?" %
", ".join([str(i) for i in not_intalled_libs]))):
ctx.invoke(cmd_lib_install, libid=not_intalled_libs)
def _autoinstall_libs(ctx, libids_list, verbose=False):
storage_dir = util.get_projectlibdeps_dir()
ctx.obj = LibraryManager(storage_dir)
if verbose:
click.echo("Library Storage: " + storage_dir)
ctx.invoke(
cmd_lib_install,
libraries=[int(l.strip()) for l in libids_list.split(",")],
quiet=not verbose)
def _clean_pioenvs_dir(pioenvs_dir):
+2 -11
View File
@@ -146,18 +146,9 @@ class APIRequestError(PlatformioException):
MESSAGE = "[API] {0}"
class LibAlreadyInstalled(PlatformioException):
pass
class LibNotFound(PlatformioException):
class LibNotInstalled(PlatformioException):
MESSAGE = "Library #{0:d} has not been installed yet"
class LibInstallDependencyError(PlatformioException):
MESSAGE = "Error has been occurred for library dependency '{0}'"
MESSAGE = "Library `{0}` has not been found in the registry"
class InvalidLibConfURL(PlatformioException):
-121
View File
@@ -1,121 +0,0 @@
# Copyright 2014-2016 Ivan Kravets <me@ikravets.com>
#
# 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 re
from os import listdir, makedirs, remove, rename
from os.path import isdir, isfile, join
from shutil import rmtree
from tempfile import gettempdir
from platformio import telemetry, util
from platformio.downloader import FileDownloader
from platformio.exception import LibAlreadyInstalled, LibNotInstalled
from platformio.unpacker import FileUnpacker
class LibraryManager(object):
CONFIG_NAME = ".library.json"
def __init__(self, lib_dir=None):
self.lib_dir = lib_dir or join(util.get_home_dir(), "lib")
@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 = {}
if not isdir(self.lib_dir):
return items
for dirname in sorted(listdir(self.lib_dir)):
conf_path = join(self.lib_dir, dirname, self.CONFIG_NAME)
if not isfile(conf_path):
continue
items[dirname] = util.load_json(conf_path)
return items
def get_latest_versions(self):
lib_ids = [str(item['id']) for item in self.get_installed().values()]
if not lib_ids:
return None
return util.get_api_result("/lib/version/" + str(",".join(lib_ids)))
def get_outdated(self):
outdated = []
for id_, latest_version in (self.get_latest_versions() or {}).items():
info = self.get_info(int(id_))
if latest_version != info['version']:
outdated.append(info['name'])
return outdated
def get_info(self, id_):
for item in self.get_installed().values():
if "id" in item and item['id'] == id_:
return item
raise LibNotInstalled(id_)
def is_installed(self, id_):
try:
return int(self.get_info(id_)['id']) == id_
except LibNotInstalled:
return False
def install(self, id_, version=None):
if self.is_installed(id_):
raise LibAlreadyInstalled()
dlinfo = util.get_api_result(
"/lib/download/" + str(id_),
dict(version=version) if version else None
)
dlpath = None
tmplib_dir = join(self.lib_dir, str(id_))
try:
dlpath = self.download(dlinfo['url'], gettempdir())
if not isdir(tmplib_dir):
makedirs(tmplib_dir)
self.unpack(dlpath, tmplib_dir)
finally:
if dlpath:
remove(dlpath)
info = self.get_info(id_)
rename(tmplib_dir, join(self.lib_dir, "%s_ID%d" % (
re.sub(r"[^\da-zA-Z]+", "_", info['name']), id_)))
telemetry.on_event(
category="LibraryManager", action="Install",
label="#%d %s" % (id_, info['name'])
)
return True
def uninstall(self, id_):
for libdir, item in self.get_installed().iteritems():
if "id" in item and item['id'] == id_:
rmtree(join(self.lib_dir, libdir))
telemetry.on_event(
category="LibraryManager", action="Uninstall",
label="#%d %s" % (id_, item['name'])
)
return True
raise LibNotInstalled(id_)
+19 -16
View File
@@ -22,12 +22,12 @@ import click
import semantic_version
from platformio import __version__, app, exception, telemetry, util
from platformio.commands.lib import lib_update as cmd_libraries_update
from platformio.commands.lib import lib_update as cmd_lib_update
from platformio.commands.platform import \
platform_install as cmd_platform_install
from platformio.commands.platform import platform_update as cmd_platform_update
from platformio.commands.upgrade import get_latest_version
from platformio.libmanager import LibraryManager
from platformio.managers.lib import LibraryManager
from platformio.managers.platform import PlatformManager
@@ -221,16 +221,12 @@ def check_internal_updates(ctx, what):
last_check[what + '_update'] = int(time())
app.set_state_item("last_check", last_check)
pm = PlatformManager() if what == "platforms" else LibraryManager()
outdated_items = []
if what == "platforms":
pm = PlatformManager()
for manifest in pm.get_installed():
if manifest['name'] not in outdated_items and \
pm.is_outdated(manifest['name']):
outdated_items.append(manifest['name'])
elif what == "libraries":
lm = LibraryManager()
outdated_items = lm.get_outdated()
for manifest in pm.get_installed():
if manifest['name'] not in outdated_items and \
pm.is_outdated(manifest['name']):
outdated_items.append(manifest['name'])
if not outdated_items:
return
@@ -245,19 +241,26 @@ def check_internal_updates(ctx, what):
if not app.get_setting("auto_update_" + what):
click.secho("Please update them via ", fg="yellow", nl=False)
click.secho("`platformio %s update`" %
("lib" if what == "libraries" else "platform"),
("lib --global" if what == "libraries" else "platform"),
fg="cyan", nl=False)
click.secho(" command.\n", fg="yellow")
click.secho("If you want to manually check for the new versions "
"without updating, please use ", fg="yellow", nl=False)
click.secho("`platformio %s update --only-check`" %
("lib --global" if what == "libraries" else "platform"),
fg="cyan", nl=False)
click.secho(" command.", fg="yellow")
else:
click.secho("Please wait while updating %s ..." % what, fg="yellow")
if what == "platforms":
ctx.invoke(cmd_platform_update)
ctx.invoke(cmd_platform_update, platforms=outdated_items)
elif what == "libraries":
ctx.invoke(cmd_libraries_update)
ctx.obj = pm
ctx.invoke(cmd_lib_update, libraries=outdated_items)
click.echo()
telemetry.on_event(category="Auto", action="Update",
label=what.title())
telemetry.on_event(
category="Auto", action="Update", label=what.title())
click.echo("*" * terminal_width)
click.echo("")
+209
View File
@@ -0,0 +1,209 @@
# Copyright 2014-present Ivan Kravets <me@ikravets.com>
#
# 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
from os.path import join
import click
import semantic_version
from platformio import app, commands, exception, util
from platformio.managers.package import BasePkgManager
class LibraryManager(BasePkgManager):
def __init__(self, package_dir=None):
if not package_dir:
package_dir = join(util.get_home_dir(), "lib")
BasePkgManager.__init__(self, package_dir)
@property
def manifest_name(self):
return ".library.json"
@staticmethod
def max_satisfying_repo_version(versions, requirements=None):
def _cmp_dates(datestr1, datestr2):
from datetime import datetime
assert "T" in datestr1 and "T" in datestr2
dateformat = "%Y-%m-%d %H:%M:%S"
date1 = datetime.strptime(datestr1[:-1].replace("T", " "),
dateformat)
date2 = datetime.strptime(datestr2[:-1].replace("T", " "),
dateformat)
if date1 == date2:
return 0
return -1 if date1 < date2 else 1
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['version'], partial=True)
except ValueError:
pass
if reqspec:
if not specver or specver not in reqspec:
continue
if not item or semantic_version.Version(
item['version'], partial=True) < specver:
item = v
elif requirements:
if requirements == v['version']:
return v
else:
if not item or _cmp_dates(item['date'], v['date']) == -1:
item = v
return item
def get_latest_repo_version(self, name, requirements):
item = self.max_satisfying_repo_version(
util.get_api_result("/lib/versions/%d" % self._get_pkg_id_by_name(
name, requirements)), requirements)
return item['version'] if item else None
def _get_pkg_id_by_name(self, name, requirements, quiet=False):
if name.startswith("id="):
return int(name[3:])
# try to find ID from installed packages
installed_dir = self.get_installed_dir(name, requirements)
if installed_dir:
manifest = self.load_manifest(installed_dir)
if "id" in manifest:
return int(manifest['id'])
return int(self.search_for_library({"name": name}, quiet)['id'])
def _install_from_piorepo(self, name, requirements):
assert name.startswith("id=")
version = self.get_latest_repo_version(name, requirements)
if not version:
raise exception.UndefinedPackageVersion(requirements or "latest",
util.get_systype())
dl_data = util.get_api_result(
"/lib/download/" + str(name[3:]), dict(version=version))
assert dl_data
pkg_dir = None
try:
pkg_dir = self._install_from_url(
name, dl_data['url'].replace("http://", "https://"),
requirements)
except exception.APIRequestError:
pkg_dir = self._install_from_url(name, dl_data['url'],
requirements)
return pkg_dir
def install(self, name, requirements=None, quiet=False,
trigger_event=True):
_name, _requirements, _url = self.parse_pkg_name(name, requirements)
if not _url:
_name = "id=%d" % self._get_pkg_id_by_name(
_name, _requirements, quiet=quiet)
already_installed = self.get_installed_dir(_name, _requirements, _url)
pkg_dir = BasePkgManager.install(self, _name if not _url else name,
_requirements, quiet, trigger_event)
if already_installed:
return
manifest = self.load_manifest(pkg_dir)
if "dependencies" not in manifest:
return pkg_dir
if not quiet:
click.secho("Installing dependencies", fg="yellow")
_dependencies = manifest['dependencies']
if not isinstance(_dependencies, list):
_dependencies = [_dependencies]
for filters in _dependencies:
assert "name" in filters
if any([s in filters.get("version", "") for s in ("\\", "/")]):
self.install("{name}={version}".format(**filters))
else:
lib_info = self.search_for_library(filters, quiet)
if filters.get("version"):
self.install(
lib_info['id'],
requirements=filters.get("version"),
quiet=quiet,
trigger_event=trigger_event)
else:
self.install(
lib_info['id'],
quiet=quiet,
trigger_event=trigger_event)
return pkg_dir
@staticmethod
def search_for_library( # pylint: disable=too-many-branches
filters, quiet=False):
assert isinstance(filters, dict)
assert "name" in filters
if not quiet:
click.echo("Looking for %s library in registry" % click.style(
filters['name'], fg="cyan"))
query = []
for key in filters:
if key not in ("name", "authors", "frameworks", "platforms"):
continue
values = filters[key]
if not isinstance(values, list):
values = [v.strip() for v in values.split(",") if v]
for value in values:
query.append('%s:"%s"' % (key[:-1] if key.endswith("s") else
key, value))
lib_info = None
result = util.get_api_result(
"/lib/search", dict(query=" ".join(query)))
if result['total'] == 1:
lib_info = result['items'][0]
elif result['total'] > 1:
click.secho(
"Conflict: More than one library has been found "
"by request %s:" % json.dumps(filters),
fg="red")
commands.lib.echo_liblist_header()
for item in result['items']:
commands.lib.echo_liblist_item(item)
if not app.get_setting("enable_prompts"):
click.echo("Automatically chose the first available library")
lib_info = result['items'][0]
else:
deplib_id = click.prompt(
"Please choose library ID",
type=click.Choice([str(i['id']) for i in result['items']]))
for item in result['items']:
if item['id'] == int(deplib_id):
lib_info = item
break
if not lib_info:
raise exception.LibNotFound(str(filters))
if not quiet:
click.echo("Found: %s" % click.style(
"http://platformio.org/lib/show/{id}/{name}".format(
**lib_info),
fg="blue"))
return lib_info
+15 -1
View File
@@ -312,7 +312,7 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
if "=" in text and not text.startswith("id="):
name, url = text.split("=", 1)
# Handle GitHub URL (https://github.com/user/package.git)
# Handle GitHub URL (https://github.com/user/package)
if url.startswith("https://github.com/") and \
not url.endswith((".zip", ".tar.gz")):
url = "git+" + url
@@ -394,6 +394,20 @@ class BasePkgManager(PkgRepoMixin, PkgInstallerMixin):
return best.get("__pkg_dir")
return None
def is_outdated(self, name, requirements=None):
installed_dir = self.get_installed_dir(name, requirements)
if not installed_dir:
click.secho(
"%s @ %s is not installed" % (name, requirements or "*"),
fg="yellow")
return
manifest_path = self.get_manifest_path(installed_dir)
if manifest_path.endswith(self.VCS_MANIFEST_NAME):
return False
manifest = self.load_manifest(installed_dir)
return manifest['version'] != self.get_latest_repo_version(
name, requirements)
def install(self, name, requirements=None, quiet=False,
trigger_event=True):
name, requirements, url = self.parse_pkg_name(name, requirements)
+3 -2
View File
@@ -71,9 +71,10 @@ class PlatformManager(BasePkgManager):
return True
def is_outdated(self, name, requirements=None):
if BasePkgManager.is_outdated(self, name, requirements):
return True
p = PlatformFactory.newPlatform(name, requirements)
return (p.are_outdated_packages() or
p.version != self.get_latest_repo_version(name, requirements))
return p.are_outdated_packages()
def cleanup_packages(self, names):
self.reset_cache()
+13 -13
View File
@@ -43,10 +43,10 @@ class VCSClientBase(object):
command = None
def __init__(self, src_dir, remote_url=None, branch=None):
def __init__(self, src_dir, remote_url=None, tag=None):
self.src_dir = src_dir
self.remote_url = remote_url
self.branch = branch
self.tag = tag
self.check_client()
def check_client(self):
@@ -71,7 +71,7 @@ class VCSClientBase(object):
@property
def can_be_updated(self):
return not self.branch
return not self.tag
def get_current_revision(self):
raise NotImplementedError
@@ -113,19 +113,19 @@ class GitClient(VCSClientBase):
@property
def can_be_updated(self):
return not self.branch or not self.is_commit_id(self.branch)
return not self.tag or not self.is_commit_id(self.tag)
def export(self):
is_commit = self.is_commit_id(self.branch)
is_commit = self.is_commit_id(self.tag)
args = ["clone", "--recursive"]
if not self.branch or not is_commit:
if not self.tag or not is_commit:
args += ["--depth", "1"]
if self.branch:
args += ["--branch", self.branch]
if self.tag:
args += ["--branch", self.tag]
args += [self.remote_url, self.src_dir]
assert self.run_cmd(args)
if is_commit:
return self.run_cmd(["reset", "--hard", self.branch])
return self.run_cmd(["reset", "--hard", self.tag])
return True
def update(self):
@@ -142,8 +142,8 @@ class HgClient(VCSClientBase):
def export(self):
args = ["clone"]
if self.branch:
args.extend(["--updaterev", self.branch])
if self.tag:
args.extend(["--updaterev", self.tag])
args.extend([self.remote_url, self.src_dir])
return self.run_cmd(args)
@@ -161,8 +161,8 @@ class SvnClient(VCSClientBase):
def export(self):
args = ["checkout"]
if self.branch:
args.extend(["--revision", self.branch])
if self.tag:
args.extend(["--revision", self.tag])
args.extend([self.remote_url, self.src_dir])
return self.run_cmd(args)