Files
platformio-core/platformio/platforms/base.py

421 lines
13 KiB
Python
Raw Normal View History

# Copyright (C) Ivan Kravets <me@ikravets.com>
# See LICENSE for details.
import re
2014-11-22 23:55:17 +02:00
from imp import load_source
from os import listdir
2014-11-29 22:39:44 +02:00
from os.path import isdir, isfile, join
import click
from platformio import exception, util
2014-11-29 22:39:44 +02:00
from platformio.app import get_state_item, set_state_item
from platformio.pkgmanager import PackageManager
PLATFORM_PACKAGES = {
"framework-arduinoavr": [
("Arduino Wiring-based Framework (AVR Core, 1.6)",
"http://arduino.cc/en/Reference/HomePage")
],
"framework-arduinosam": [
("Arduino Wiring-based Framework (SAM Core, 1.6)",
"http://arduino.cc/en/Reference/HomePage")
],
"framework-arduinoteensy": [
("Arduino Wiring-based Framework",
"http://arduino.cc/en/Reference/HomePage")
],
"framework-arduinomsp430": [
("Arduino Wiring-based Framework (MSP430 Core)",
"http://arduino.cc/en/Reference/HomePage")
],
"framework-arduinoespressif": [
("Arduino Wiring-based Framework (ESP8266 Core)",
"https://github.com/esp8266/Arduino")
],
"framework-energiamsp430": [
("Energia Wiring-based Framework (MSP430 Core)",
"http://energia.nu/reference/")
],
"framework-energiativa": [
("Energia Wiring-based Framework (LM4F Core)",
"http://energia.nu/reference/")
],
"framework-cmsis": [
("Vendor-independent hardware abstraction layer for the Cortex-M "
"processor series",
"http://www.arm.com/products/processors/"
"cortex-m/cortex-microcontroller-software-interface-standard.php")
],
"framework-spl": [
("Standard Peripheral Library for STM32 MCUs",
"http://www.st.com"
"/web/catalog/tools/FM147/CL1794/SC961/SS1743/PF257890")
],
"framework-libopencm3": [
("libOpenCM3 Framework", "http://www.libopencm3.org/")
],
"framework-mbed": [
("mbed Framework", "http://mbed.org")
],
"sdk-esp8266": [
("ESP8266 SDK", "http://bbs.espressif.com")
],
"ldscripts": [
("Linker Scripts",
"https://sourceware.org/binutils/docs/ld/Scripts.html")
],
"toolchain-atmelavr": [
("avr-gcc", "https://gcc.gnu.org/wiki/avr-gcc"),
("GDB", "http://www.gnu.org/software/gdb/"),
("AVaRICE", "http://avarice.sourceforge.net/"),
("SimulAVR", "http://www.nongnu.org/simulavr/")
],
"toolchain-gccarmnoneeabi": [
("gcc-arm-embedded", "https://launchpad.net/gcc-arm-embedded"),
("GDB", "http://www.gnu.org/software/gdb/")
],
"toolchain-gccarmlinuxgnueabi": [
("GCC for Linux ARM GNU EABI", "https://gcc.gnu.org"),
("GDB", "http://www.gnu.org/software/gdb/")
],
"toolchain-gccmingw32": [
("MinGW", "http://www.mingw.org")
],
"toolchain-gcclinux32": [
("GCC for Linux i686", "https://gcc.gnu.org")
],
"toolchain-gcclinux64": [
("GCC for Linux x86_64", "https://gcc.gnu.org")
],
"toolchain-xtensa": [
("xtensa-gcc", "https://github.com/jcmvbkbc/gcc-xtensa"),
("GDB", "http://www.gnu.org/software/gdb/")
],
"toolchain-timsp430": [
("msp-gcc", "http://sourceforge.net/projects/mspgcc/"),
("GDB", "http://www.gnu.org/software/gdb/")
],
"tool-avrdude": [
("AVRDUDE", "http://www.nongnu.org/avrdude/")
],
"tool-micronucleus": [
("Micronucleus", "https://github.com/micronucleus/micronucleus")
],
"tool-bossac": [
("BOSSA CLI", "https://sourceforge.net/projects/b-o-s-s-a/")
],
"tool-stlink": [
("ST-Link", "https://github.com/texane/stlink")
],
"tool-teensy": [
("Teensy Loader", "https://www.pjrc.com/teensy/loader.html")
],
"tool-lm4flash": [
("Flash Programmer", "http://www.ti.com/tool/lmflashprogrammer")
],
"tool-mspdebug": [
("MSPDebug", "http://mspdebug.sourceforge.net/")
],
"tool-esptool": [
("esptool-ck", "https://github.com/igrr/esptool-ck")
]
}
def get_packages():
return PLATFORM_PACKAGES
class PlatformFactory(object):
@staticmethod
2015-03-16 14:15:57 +02:00
def get_clsname(type_):
2015-04-20 18:48:38 +01:00
return "%s%sPlatform" % (type_.upper()[0], type_.lower()[1:])
2014-11-22 23:55:17 +02:00
@staticmethod
2015-03-16 14:15:57 +02:00
def load_module(type_, path):
2014-11-29 22:39:44 +02:00
module = None
try:
module = load_source(
2015-03-16 14:15:57 +02:00
"platformio.platforms.%s" % type_, path)
2014-11-29 22:39:44 +02:00
except ImportError:
2015-03-16 14:15:57 +02:00
raise exception.UnknownPlatform(type_)
2014-11-29 22:39:44 +02:00
return module
@classmethod
@util.memoized
def _lookup_platforms(cls):
2014-11-22 23:55:17 +02:00
platforms = {}
for d in (util.get_home_dir(), util.get_source_dir()):
pdir = join(d, "platforms")
if not isdir(pdir):
continue
2015-05-27 19:21:29 +03:00
for p in sorted(listdir(pdir)):
if (p in ("__init__.py", "base.py") or not
p.endswith(".py")):
continue
type_ = p[:-3]
path = join(pdir, p)
try:
isplatform = hasattr(
cls.load_module(type_, path),
cls.get_clsname(type_)
)
if isplatform:
platforms[type_] = path
except exception.UnknownPlatform:
pass
return platforms
@classmethod
def get_platforms(cls, installed=False):
platforms = cls._lookup_platforms()
2014-11-22 23:55:17 +02:00
if not installed:
return platforms
installed_platforms = {}
2015-03-16 14:15:57 +02:00
for type_ in get_state_item("installed_platforms", []):
if type_ in platforms:
installed_platforms[type_] = platforms[type_]
2014-11-22 23:55:17 +02:00
return installed_platforms
2014-11-29 22:39:44 +02:00
@classmethod
2015-03-16 14:15:57 +02:00
def newPlatform(cls, type_):
2014-11-29 22:39:44 +02:00
platforms = cls.get_platforms()
2015-03-16 14:15:57 +02:00
if type_ not in platforms:
raise exception.UnknownPlatform(type_)
_instance = getattr(
2015-03-16 14:15:57 +02:00
cls.load_module(type_, platforms[type_]),
cls.get_clsname(type_)
)()
assert isinstance(_instance, BasePlatform)
return _instance
class BasePlatform(object):
PACKAGES = {}
LINE_ERROR_RE = re.compile(r"(\s+error|error[:\s]+)", re.I)
def __init__(self):
self._found_error = False
self._last_echo_line = None
# 1 = errors
# 2 = 1 + warnings
# 3 = 2 + others
self._verbose_level = 3
2015-03-16 14:15:57 +02:00
def get_type(self):
return self.__class__.__name__[:-8].lower()
2015-03-16 14:15:57 +02:00
def get_name(self):
return self.get_type().title()
2014-11-22 23:55:17 +02:00
def get_build_script(self):
builtin = join(util.get_source_dir(), "builder", "scripts", "%s.py" %
2015-03-16 14:15:57 +02:00
self.get_type())
2014-11-22 23:55:17 +02:00
if isfile(builtin):
return builtin
raise NotImplementedError()
def get_description(self):
if self.__doc__:
doclines = [l.strip() for l in self.__doc__.splitlines() if
l.strip()]
return " ".join(doclines[:-1]).strip()
else:
raise NotImplementedError()
def get_vendor_url(self):
if self.__doc__ and "http" in self.__doc__:
return self.__doc__[self.__doc__.index("http"):].strip()
else:
raise NotImplementedError()
def is_embedded(self):
for name, opts in self.get_packages().items():
if name == "framework-mbed" or opts.get("alias") == "uploader":
return True
return False
2014-11-22 23:55:17 +02:00
def get_packages(self):
return self.PACKAGES
def get_pkg_alias(self, pkgname):
return self.PACKAGES[pkgname].get("alias", None)
def pkg_aliases_to_names(self, aliases):
names = []
for alias in aliases:
name = alias
2014-11-22 23:55:17 +02:00
# lookup by package aliases
for _name, _opts in self.get_packages().items():
if _opts.get("alias", None) == alias:
name = _name
break
names.append(name)
return names
2014-07-30 22:40:11 +03:00
def get_default_packages(self):
return [k for k, v in self.get_packages().items()
if v.get("default", False)]
2014-11-22 23:55:17 +02:00
def get_installed_packages(self):
pm = PackageManager()
return [n for n in self.get_packages().keys() if pm.is_installed(n)]
2014-07-30 22:40:11 +03:00
def install(self, with_packages, without_packages, skip_default_packages):
with_packages = set(self.pkg_aliases_to_names(with_packages))
without_packages = set(self.pkg_aliases_to_names(without_packages))
2014-07-30 22:40:11 +03:00
upkgs = with_packages | without_packages
2014-11-22 23:55:17 +02:00
ppkgs = set(self.get_packages().keys())
if not upkgs.issubset(ppkgs):
raise exception.UnknownPackage(", ".join(upkgs - ppkgs))
2014-07-30 22:40:11 +03:00
requirements = []
2014-11-22 23:55:17 +02:00
for name, opts in self.get_packages().items():
if name in without_packages:
continue
2014-07-30 22:40:11 +03:00
elif (name in with_packages or (not skip_default_packages and
opts['default'])):
2014-11-22 23:55:17 +02:00
requirements.append(name)
pm = PackageManager()
for name in requirements:
pm.install(name)
# register installed platform
2014-11-29 22:39:44 +02:00
data = get_state_item("installed_platforms", [])
2015-03-16 14:15:57 +02:00
if self.get_type() not in data:
data.append(self.get_type())
2014-11-29 22:39:44 +02:00
set_state_item("installed_platforms", data)
2014-07-30 22:40:11 +03:00
return len(requirements)
def uninstall(self):
2015-03-16 14:15:57 +02:00
platform = self.get_type()
2014-11-22 23:55:17 +02:00
installed_platforms = PlatformFactory.get_platforms(
installed=True).keys()
if platform not in installed_platforms:
raise exception.PlatformNotInstalledYet(platform)
2014-11-22 23:55:17 +02:00
deppkgs = set()
for item in installed_platforms:
if item == platform:
continue
2014-12-03 20:16:50 +02:00
p = PlatformFactory.newPlatform(item)
2014-11-22 23:55:17 +02:00
deppkgs = deppkgs.union(set(p.get_packages().keys()))
pm = PackageManager()
for name in self.get_packages().keys():
2014-11-24 22:24:19 +02:00
if not pm.is_installed(name) or name in deppkgs:
2014-11-22 23:55:17 +02:00
continue
pm.uninstall(name)
# unregister installed platform
2014-11-29 22:39:44 +02:00
installed_platforms.remove(platform)
set_state_item("installed_platforms", installed_platforms)
return True
def update(self):
2014-11-22 23:55:17 +02:00
pm = PackageManager()
for name in self.get_installed_packages():
pm.update(name)
2014-11-29 22:39:44 +02:00
def is_outdated(self):
pm = PackageManager()
obsolated = pm.get_outdated()
return not set(self.get_packages().keys()).isdisjoint(set(obsolated))
def run(self, variables, targets, verbose):
assert isinstance(variables, list)
assert isinstance(targets, list)
self._verbose_level = int(verbose)
2014-11-22 23:55:17 +02:00
installed_platforms = PlatformFactory.get_platforms(
installed=True).keys()
installed_packages = PackageManager.get_installed()
2015-03-16 14:15:57 +02:00
if self.get_type() not in installed_platforms:
raise exception.PlatformNotInstalledYet(self.get_type())
2014-11-22 23:55:17 +02:00
if "clean" in targets:
targets.remove("clean")
targets.append("-c")
2014-11-22 23:55:17 +02:00
if not any([v.startswith("BUILD_SCRIPT=") for v in variables]):
variables.append("BUILD_SCRIPT=%s" % self.get_build_script())
for v in variables:
if not v.startswith("BUILD_SCRIPT="):
continue
_, path = v.split("=", 2)
if not isfile(path):
raise exception.BuildScriptNotFound(path)
2014-11-22 23:55:17 +02:00
# append aliases of the installed packages
2014-11-22 23:55:17 +02:00
for name, options in self.get_packages().items():
2015-02-03 18:44:24 +02:00
if "alias" not in options or name not in installed_packages:
2014-11-22 23:55:17 +02:00
continue
variables.append(
"PIOPACKAGE_%s=%s" % (options['alias'].upper(), name))
self._found_error = False
try:
result = util.exec_command(
[
"scons",
"-Q",
"-f", join(util.get_source_dir(), "builder", "main.py")
] + variables + targets,
stdout=util.AsyncPipe(self.on_run_out),
stderr=util.AsyncPipe(self.on_run_err)
)
except OSError:
raise exception.SConsNotInstalled()
assert "returncode" in result
if self._found_error:
result['returncode'] = 1
if self._last_echo_line == ".":
click.echo("")
return result
def on_run_out(self, line):
self._echo_line(line, level=3)
def on_run_err(self, line):
is_error = self.LINE_ERROR_RE.search(line) is not None
if is_error:
self._found_error = True
self._echo_line(line, level=1 if is_error else 2)
def _echo_line(self, line, level):
assert 1 <= level <= 3
fg = ("red", "yellow", None)[level - 1]
if level == 3 and "is up to date" in line:
fg = "green"
if level > self._verbose_level:
click.secho(".", fg=fg, err=level < 3, nl=False)
self._last_echo_line = "."
return
if self._last_echo_line == ".":
click.echo("")
self._last_echo_line = line
click.secho(line, fg=fg, err=level < 3)