Allow to install development platform from local directory // Issue #479

This commit is contained in:
Ivan Kravets
2016-05-26 22:33:17 +03:00
parent d68eb28629
commit 9a1f1ddb8b
5 changed files with 101 additions and 32 deletions

View File

@ -21,14 +21,17 @@ Usage
.. code-block:: bash
# install platform by name
platformio platform install [OPTIONS] [PLATFORMS]
# install platform from local directory
platformio platform install [OPTIONS] [file:///local/path/to/platform/dir]
Description
-----------
Install pre-built development :ref:`platforms` with related
packages.
Install development :ref:`platforms` and dependent packages.
There are several predefined aliases for packages, such as:

View File

@ -53,7 +53,7 @@ class FileDownloader(object):
return self._destination
def get_lmtime(self):
return self._request.headers['last-modified']
return self._request.headers.get("last-modified")
def get_size(self):
if "content-length" not in self._request.headers:
@ -77,7 +77,8 @@ class FileDownloader(object):
f.close()
self._request.close()
self._preserve_filemtime(self.get_lmtime())
if self.get_lmtime():
self._preserve_filemtime(self.get_lmtime())
def verify(self, sha1=None):
_dlsize = getsize(self._destination)

View File

@ -70,6 +70,11 @@ class UnknownPackage(PlatformioException):
MESSAGE = "Detected unknown package '{0}'"
class InvalidLocalPackage(PlatformioException):
MESSAGE = "Invalid local package '{0}'. Can not find manifest '{1}'"
class UndefinedPackageVersion(PlatformioException):
MESSAGE = "Can not find package '{0}' with version requirements '{1}'"\

View File

@ -14,7 +14,7 @@
import os
from os.path import dirname, isdir, isfile, join
from shutil import rmtree
from shutil import copyfile, copytree, rmtree
import click
import requests
@ -41,6 +41,10 @@ class PackageManager(object):
def reset_cache():
PackageManager._INSTALLED_CACHE = {}
@staticmethod
def get_manifest_name():
return "package.json"
@staticmethod
def download(url, dest_dir, sha1=None):
fd = FileDownloader(url, dest_dir)
@ -50,13 +54,56 @@ class PackageManager(object):
return fd.get_filepath()
@staticmethod
def unpack(pkgpath, dest_dir):
fu = FileUnpacker(pkgpath, dest_dir)
def unpack(source_path, dest_dir):
fu = FileUnpacker(source_path, dest_dir)
return fu.start()
@staticmethod
def get_manifest_name():
return "package.json"
def check_structure(self, pkg_dir):
if isfile(join(pkg_dir, self.get_manifest_name())):
return True
for root, _, files in os.walk(pkg_dir):
if self.get_manifest_name() not in files:
continue
# copy contents to the root of package directory
for item in os.listdir(root):
item_path = join(root, item)
if isfile(item_path):
copyfile(item_path, join(pkg_dir, item))
elif isdir(item_path):
copytree(item_path, join(pkg_dir, item), symlinks=True)
# remove not used contents
while True:
rmtree(root)
root = dirname(root)
if root == pkg_dir:
break
break
if isfile(join(pkg_dir, self.get_manifest_name())):
return True
raise exception.PlatformioException(
"Could not find '%s' manifest file in the package" %
self.get_manifest_name())
def make_pkg_dir(self, name, version):
pkg_dir = join(self.package_dir, name)
if isfile(join(pkg_dir, self.get_manifest_name())):
_manifest = util.load_json(
join(pkg_dir, self.get_manifest_name()))
if (_manifest['name'] == name and
_manifest['version'] != version):
pkg_dir = join(
self.package_dir, "%s@%s" % (name, version))
# remove previous/not-satisfied package
if isdir(pkg_dir):
rmtree(pkg_dir)
os.makedirs(pkg_dir)
assert isdir(pkg_dir)
return pkg_dir
@staticmethod
def max_satisfying_version(versions, requirements=None):
@ -114,7 +161,8 @@ class PackageManager(object):
def install(self, name, requirements, silent=False, trigger_event=True):
installed = self.is_installed(name, requirements)
if not installed or not silent:
click.echo("Installing package %s @ %s:" % (
click.echo("Installing %s %s @ %s:" % (
self.get_manifest_name().split(".")[0],
click.style(name, fg="cyan"),
requirements if requirements else "latest"))
if installed:
@ -122,18 +170,24 @@ class PackageManager(object):
click.secho("Already installed", fg="yellow")
return
if not self._install_from_piorepo(name, requirements):
manifest_path = None
if name.startswith("file://"):
manifest_path = self._install_from_local_dir(name[7:])
else:
manifest_path = self._install_from_piorepo(name, requirements)
if not isfile(manifest_path):
raise exception.PackageInstallError(
name, requirements, util.get_systype())
name, requirements or "latest", util.get_systype())
self.reset_cache()
if trigger_event:
telemetry.on_event(
category="PackageManager", action="Install", label=name)
return manifest_path
def _install_from_piorepo(self, name, requirements):
pkg_dir = None
success = False
pkgdata = None
versions = None
for versions in PackageRepoIterator(name, self.repositories):
@ -142,26 +196,17 @@ class PackageManager(object):
if not pkgdata:
continue
pkg_dir = join(self.package_dir, name)
if isfile(join(pkg_dir, self.get_manifest_name())):
pkg_dir = join(
self.package_dir, "%s@%s" % (name, pkgdata['version']))
# remove previous/not-satisfied package
if isdir(pkg_dir):
rmtree(pkg_dir)
os.makedirs(pkg_dir)
pkg_dir = self.make_pkg_dir(name, pkgdata['version'])
try:
dlpath = self.download(
pkgdata['url'], pkg_dir, pkgdata.get("sha1"))
assert isfile(dlpath)
self.unpack(dlpath, pkg_dir)
success = True
self.check_structure(pkg_dir)
break
except Exception as e: # pylint: disable=broad-except
click.secho("Warning! Package Mirror: %s" % e, fg="yellow")
click.secho("Looking for other Package Mirror...", fg="yellow")
click.secho("Looking for another mirror...", fg="yellow")
finally:
if dlpath and isfile(dlpath):
os.remove(dlpath)
@ -170,12 +215,26 @@ class PackageManager(object):
raise exception.UnknownPackage(name)
elif not pkgdata:
raise exception.UndefinedPackageVersion(
name, requirements, util.get_systype())
name, requirements or "latest", util.get_systype())
return success
return join(pkg_dir, self.get_manifest_name())
def _install_from_local_dir(self, local_dir):
if not isfile(join(local_dir, self.get_manifest_name())):
raise exception.InvalidLocalPackage(
local_dir, self.get_manifest_name())
manifest = util.load_json(join(local_dir, self.get_manifest_name()))
assert set(["name", "version"]) <= set(manifest.keys())
pkg_dir = self.make_pkg_dir(manifest['name'], manifest['version'])
rmtree(pkg_dir)
copytree(local_dir, pkg_dir, symlinks=True)
return join(pkg_dir, self.get_manifest_name())
def uninstall(self, name, requirements=None, trigger_event=True):
click.echo("Uninstalling package %s @ %s: \t" % (
click.echo("Uninstalling %s %s @ %s: \t" % (
self.get_manifest_name().split(".")[0],
click.style(name, fg="cyan"),
requirements if requirements else "latest"), nl=False)
found = False
@ -201,7 +260,8 @@ class PackageManager(object):
category="PackageManager", action="Uninstall", label=name)
def update(self, name, requirements=None, keep_versions=None):
click.echo("Updating package %s @ %s:" % (
click.echo("Updating %s %s @ %s:" % (
self.get_manifest_name().split(".")[0],
click.style(name, fg="yellow"),
requirements if requirements else "latest"))

View File

@ -56,9 +56,9 @@ class PlatformManager(PackageManager):
def install(self, # pylint: disable=too-many-arguments,arguments-differ
name, requirements=None, with_packages=None,
without_packages=None, skip_default_packages=False):
PackageManager.install(self, name, requirements)
manifest_path = PackageManager.install(self, name, requirements)
return PlatformFactory.newPlatform(
name, requirements).install_packages(
manifest_path, requirements).install_packages(
with_packages, without_packages, skip_default_packages)
def uninstall(self, # pylint: disable=arguments-differ