Skip to content

Commit

Permalink
Merge pull request #699 from flit/bugfix/no_cpm
Browse files Browse the repository at this point in the history
Make cmsis-pack-manager optional
  • Loading branch information
flit authored Jul 20, 2019
2 parents 289ada6 + d07c152 commit 15e770f
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 2 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ You have a few options here:
3. Run the command in a [virtualenv](https://virtualenv.pypa.io/en/latest/)
local to a specific project working set.

For notes about installing and using on non-x86 systems such as Raspberry Pi, see the
[relevant documentation](docs/installing_on_non_x86.md).

### libusb installation

[pyusb](https://github.com/pyusb/pyusb) and its backend library [libusb](https://libusb.info/) are
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- [Debugging multicore devices](multicore_debug.md)
- [Introduction to the pyOCD Python API](python_api.md)
- [Python API examples](api_examples.md)
- [Installing on non-x86 platforms](installing_on_non_x86.md)

### Developer documentation

Expand Down
55 changes: 55 additions & 0 deletions docs/installing_on_non_x86.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
Installing on non-x86 platforms
===============================

pyOCD itself is pure Python and should run on any platform with a modern Python installation.
However, it has several dependencies with binary backends that cause trouble on non-x86 platforms or
alternative operating systems (i.e., not macOS, Linux, or Windows). The most common "non-standard"
system that users would like to run pyOCD on is the Raspberry Pi, as well as similar, Arm-based
single-board computers.


## cmsis-pack-manager

The main dependency that causes trouble is
[cmsis-pack-manager](https://github.com/armmbed/cmsis-pack-manager/). It has a backend written in
the Rust language to greatly improve performance. Unfortunately, wheels are not available for
non-x86 systems. And, worse, the Rust compiler runs out of memory and dies when attempting to build
on small platforms such as the Raspberry Pi. (Cross-compilation may be an option but has not been
sufficiently investigated.)

The good news is that cmsis-pack-manager is optional for pyOCD. Although it is listed as a required
dependency in `setup.py` (so the vast majority of users benefit from it), pyOCD can run without it
with minimal loss of functionality. The `pack` subcommand is disabled, which removes automatic
CMSIS-Pack download and management. But you can still use CMSIS-Packs manually with the `--pack`
option, as described in [Target support](target_support.md).

To install pyOCD on such a system, you need to use the `pip install --no-deps` option. This will
install the core pyOCD package only, so all additional requirements must be manually installed.
You can either run `pip install` with an explicit list of the requirements from `setup.py`, or
copy those requirements to a requirements text file and run `pip install -r reqs.txt`. Of course,
be sure to exclude cmsis-pack-manager! (The requirements are not listed here lest they get out of
sync with `setup.py`.)

Once pyOCD is successfully installed, you will need to run it as a module using the `python`
executable as `python -mpyocd` rather than running the `pyocd` executable directly. This is because
the `pyocd` executable, which is auto-generated by the install process, verifies dependencies and
will error out due to the missing cmsis-pack-manager. But when run through `python`, dependencies
are not checked and pyOCD can handle the lack of cmsis-pack-manager gracefully.


## USB interfaces

The other dependencies that might be problematic are those for USB communications. pyOCD uses
three USB libraries:

- pyusb and libusb: CMSIS-DAPv1 on Linux, CMSIS-DAPv2 and STLink on all OSes
- hidapi: CMSIS-DAPv1 on macOS
- pywinusb: CMSIS-DAPv1 on Windows

Thankfully, libusb is provided by default on most Linux systems, including Raspberry Pi and similar
platforms. So on almost all platforms, USB is not a real problem.

If you are using an alternative OS, you may have trouble finding a functional USB library. In this
case, the options would be to port libusb and/or hidapi to your OS, or even to extend pyOCD with
support for another USB interface package.

11 changes: 10 additions & 1 deletion pyocd/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import fnmatch
import re
import prettytable
import cmsis_pack_manager

from . import __version__
from .core.session import Session
Expand All @@ -48,6 +47,12 @@
from .core import options
from .utility.cmdline import split_command_line

try:
import cmsis_pack_manager
CPM_AVAILABLE = True
except ImportError:
CPM_AVAILABLE = False

## @brief Default log format for all subcommands.
LOG_FORMAT = "%(relativeCreated)07d:%(levelname)s:%(module)s:%(message)s"

Expand Down Expand Up @@ -595,6 +600,10 @@ def do_commander(self):

def do_pack(self):
"""! @brief Handle 'pack' subcommand."""
if not CPM_AVAILABLE:
LOG.error("'pack' command is not available because cmsis-pack-manager is not installed")
return

verbosity = self._args.verbose - self._args.quiet
cache = cmsis_pack_manager.Cache(verbosity < 0, False)

Expand Down
11 changes: 10 additions & 1 deletion pyocd/target/pack/pack_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
# limitations under the License.

from __future__ import print_function
import cmsis_pack_manager
import logging
import six
import os
Expand All @@ -27,6 +26,12 @@
from ...debug.svd.loader import SVDFile
from ...utility.compatibility import FileNotFoundError_

try:
import cmsis_pack_manager
CPM_AVAILABLE = True
except ImportError:
CPM_AVAILABLE = False

LOG = logging.getLogger(__name__)

class ManagedPacks(object):
Expand All @@ -40,6 +45,8 @@ class ManagedPacks(object):
@staticmethod
def get_installed_packs(cache=None):
"""! @brief Return a list containing CmsisPackRef objects for all installed packs."""
if not CPM_AVAILABLE:
return []
cache = cache or cmsis_pack_manager.Cache(True, True)
results = []
# packs_for_devices() returns only unique packs.
Expand All @@ -55,6 +62,8 @@ def get_installed_packs(cache=None):
@staticmethod
def get_installed_targets():
"""! @brief Return a list of CmsisPackDevice objects for installed pack targets."""
if not CPM_AVAILABLE:
return []
cache = cmsis_pack_manager.Cache(True, True)
results = []
for pack in ManagedPacks.get_installed_packs(cache=cache):
Expand Down

0 comments on commit 15e770f

Please sign in to comment.