Newer
Older
mbed-os / tools / arm_pack_manager / __init__.py
"""
Copyright (c) 2016-2019 ARM Limited. All rights reserved.

SPDX-License-Identifier: Apache-2.0

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 os.path import join, dirname
from json import load, dump
import warnings

try:
    from cmsis_pack_manager import Cache as _Cache
    _CPM_PRESENT = True
except (ImportError, OSError):
    _CPM_PRESENT = False

from tools.flash_algo import PackFlashAlgo

warnings.filterwarnings("ignore")

RootPackURL = "http://www.keil.com/pack/index.idx"

LocalPackDir = dirname(__file__)
LocalPackIndex = join(LocalPackDir, "index.json")
LocalPackAliases = join(LocalPackDir, "aliases.json")
LocalPackLegacyNames = join(LocalPackDir, "legacy-names.json")


class _CacheLookup(object):
    def __init__(self, index, legacy_names):
        self.index = index
        self.legacy_names = legacy_names

    def __getitem__(self, name):
        try:
            return self.index[name]
        except KeyError:
            return self.index[self.legacy_names[name]]

    def __contains__(self, name):
        return name in self.index or name in self.legacy_names


class Cache(object):
    """ The Cache object is the only relevant API object at the moment

    Constructing the Cache object does not imply any caching.
    A user of the API must explicitly call caching functions.

    :param silent: Not used
    :type silent: bool
    :param no_timeouts: Not used
    :type no_timeouts: bool
    """
    def __init__(self, silent, no_timeouts):
        if _CPM_PRESENT:
            self._cache = _Cache(
                silent, no_timeouts,
                json_path=LocalPackDir, data_path=LocalPackDir
            )
        else:
            self._cache = None

        try:
            self._legacy_names = load(open(LocalPackLegacyNames))
        except IOError:
            self._legacy_names = {}

    def _get_sectors(self, device):
        """Extract sector sizes from device FLM algorithm

        Will return None if there is no algorithm, pdsc URL formatted in
        correctly

        :return: A list tuples of sector start and size
        :rtype: [list]
        """
        try:
            pack = self._cache.pack_from_cache(device)
            ret = []
            for algo in device['algorithms']:
                try:
                    flm = pack.open(
                        algo["file_name"]
                        .replace("\\\\", "/")
                        .replace("\\", "/")
                    )
                    flash_alg = PackFlashAlgo(flm.read())
                    sectors = [(flash_alg.flash_start + offset, size)
                               for offset, size in flash_alg.sector_sizes]
                    ret.extend(sectors)
                except Exception:
                    pass
            ret.sort(key=lambda sector: sector[0])
            return ret
        except Exception:
            return None

    @property
    def index(self):
        if _CPM_PRESENT:
            return _CacheLookup(self._cache.index, self._legacy_names)
        else:
            local_index = load(open(LocalPackIndex))
            return _CacheLookup(local_index, self._legacy_names)

    def cache_descriptors(self):
        if _CPM_PRESENT:
            return self._cache.cache_descriptors()
        else:
            print(
                'The Python package "cmsis-pack-manager" is not installed. '
                'To cache CMSIS Pack descriptors, please install this '
                'package with "pip install cmsis-pack-manager".'
            )
            return []

    def cache_everything(self):
        if _CPM_PRESENT:
            self._cache.cache_everything()
            for name, device in self._cache.index.items():
                if name != "version":
                    device["sectors"] = self._get_sectors(device)
            self.generate_index()
        else:
            print(
                'The Python package "cmsis-pack-manager" is not installed. '
                'To update the cache, please install this package with '
                '"pip install cmsis-pack-manager".'
            )

    def get_svd_file(self, device_name):
        """Retrieve the flash algorithm file for a particular part.

        Assumes that both the PDSC and the PACK file associated with that part
        are in the cache.

        :param device_name: The exact name of a device
        :type device_name: str
        :return: A file-like object that, when read, is the ELF file that
                 describes the flashing algorithm
        :rtype: ZipExtFile
        """
        if _CPM_PRESENT:
            device = self.index[device_name]
            pack = self._cache.pack_from_cache(device)
            return pack.open(device['debug'])
        else:
            print(
                'The Python package "cmsis-pack-manager" is not installed. '
                'To use SVD files, please install this package with '
                '"pip install cmsis-pack-manager".'
            )
            return None

    def generate_index(self):
        if _CPM_PRESENT:
            with open(LocalPackIndex, "w+") as out:
                self._cache.index["version"] = "0.2.0"
                dump(self._cache.index, out, indent=4, sort_keys=True)
        else:
            print(
                'The Python package "cmsis-pack-manager" is not installed. '
                'To generate a CMSIS Pack index, please install this package with '
                '"pip install cmsis-pack-manager".'
            )