diff --git a/pyocd/target/pack/pack.py b/pyocd/target/pack/cmsis_pack.py similarity index 89% rename from pyocd/target/pack/pack.py rename to pyocd/target/pack/cmsis_pack.py index 375686331..b1d09c827 100644 --- a/pyocd/target/pack/pack.py +++ b/pyocd/target/pack/cmsis_pack.py @@ -109,6 +109,7 @@ def devices(self): def _parse_devices(self, parent): # Extract device description elements we care about. newState = _DeviceInfo(element=parent) + children = [] for elem in parent: if elem.tag == 'memory': newState.memories.append(elem) @@ -116,6 +117,9 @@ def _parse_devices(self, parent): newState.algos.append(elem) elif elem.tag == 'debug': newState.debugs.append(elem) + # Save any elements that we will recurse into. + elif elem.tag in ('subFamily', 'device', 'variant'): + children.append(elem) # Push the new device description state onto the stack. self._state_stack.append(newState) @@ -134,7 +138,7 @@ def _parse_devices(self, parent): self._devices.append(dev) # Recursively process subelements. - for elem in [e for e in parent if e.tag in ('subFamily', 'device', 'variant')]: + for elem in children: self._parse_devices(elem) self._state_stack.pop() @@ -149,68 +153,65 @@ def _extract_families(self): families += [elem.attrib['DsubFamily']] return families - def _extract_memories(self): + def _extract_items(self, state_info_name, filter): map = {} for state in self._state_stack: - for elem in state.memories: + for elem in getattr(state, state_info_name): try: - if 'name' in elem.attrib: - name = elem.attrib['name'] - elif 'id' in elem.attrib: - name = elem.attrib['id'] - else: - # Neither option for memory name was specified, so skip this region. - LOG.debug("skipping unnamed memmory region") - continue - - map[name] = elem - except KeyError as err: + filter(map, elem) + except (KeyError, ValueError) as err: LOG.debug("error parsing CMSIS-Pack: " + str(err)) return list(map.values()) + def _extract_memories(self): + def filter(map, elem): + if 'name' in elem.attrib: + name = elem.attrib['name'] + elif 'id' in elem.attrib: + name = elem.attrib['id'] + else: + # Neither option for memory name was specified, so skip this region. + LOG.debug("skipping unnamed memmory region") + return + + map[name] = elem + + return self._extract_items('memories', filter) + def _extract_algos(self): - algos = {} - for state in self._state_stack: - for elem in state.algos: - try: - # We only support Keil FLM style flash algorithms (for now). - if ('style' in elem.attrib) and (elem.attrib['style'] != 'Keil'): - LOG.debug("skipping non-Keil flash algorithm") - continue - - # Both start and size are required. - start = int(elem.attrib['start'], base=0) - size = int(elem.attrib['size'], base=0) - memrange = (start, size) - - # An algo with the same range as an existing algo will override the previous. - algos[memrange] = elem - except (KeyError, ValueError) as err: - # Ignore errors. - LOG.debug("error parsing CMSIS-Pack: " + str(err)) - return list(algos.values()) + def filter(map, elem): + # We only support Keil FLM style flash algorithms (for now). + if ('style' in elem.attrib) and (elem.attrib['style'] != 'Keil'): + LOG.debug("skipping non-Keil flash algorithm") + return None, None + + # Both start and size are required. + start = int(elem.attrib['start'], base=0) + size = int(elem.attrib['size'], base=0) + memrange = (start, size) + + # An algo with the same range as an existing algo will override the previous. + map[memrange] = elem + + return self._extract_items('algos', filter) def _extract_debugs(self): - debugs = {} - for state in self._state_stack: - for elem in state.debugs: - try: - if 'Pname' in elem.attrib: - name = elem.attrib['Pname'] - unit = elem.attrib.get('Punit', 0) - name += str(unit) - - if '*' in debugs: - debug = {} - debugs[name] = elem - else: - # No processor name was provided, so this debug element applies to - # all processors. - debugs = {'*': elem} - except (KeyError, ValueError) as err: - # Ignore errors. - LOG.debug("error parsing CMSIS-Pack: " + str(err)) - return list(debugs.values()) + def filter(map, elem): + if 'Pname' in elem.attrib: + name = elem.attrib['Pname'] + unit = elem.attrib.get('Punit', 0) + name += str(unit) + + if '*' in map: + map.clear() + map[name] = elem + else: + # No processor name was provided, so this debug element applies to + # all processors. + map.clear() + map['*'] = elem + + return self._extract_items('debugs', filter) def get_file(self, filename): """! @brief Return file-like object for a file within the pack. diff --git a/pyocd/target/pack/pack_target.py b/pyocd/target/pack/pack_target.py index 60abe1f7a..a376d0b11 100644 --- a/pyocd/target/pack/pack_target.py +++ b/pyocd/target/pack/pack_target.py @@ -18,7 +18,7 @@ import logging import six -from .pack import (CmsisPack, MalformedCmsisPackError) +from .cmsis_pack import (CmsisPack, MalformedCmsisPackError) from ..family import FAMILIES from .. import TARGET from ...core.coresight_target import CoreSightTarget @@ -57,7 +57,9 @@ def _find_family_class(dev): # Scan each level of families for familyName in dev.families: for regex in familyInfo.matches: - if regex.fullmatch(familyName): + # Require the regex to match the entire family name. + match = regex.match(familyName) + if match and match.span() == (0, len(familyName)): return familyInfo.klass else: # Default target superclass. diff --git a/pyocd/test/test_py3_helpers.py b/pyocd/test/test_py3_helpers.py new file mode 100644 index 000000000..88316e69a --- /dev/null +++ b/pyocd/test/test_py3_helpers.py @@ -0,0 +1,51 @@ +# pyOCD debugger +# Copyright (c) 2019 Arm Ltd +# +# 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 pyocd.utility.py3_helpers import ( + PY3, + iter_single_bytes, + to_bytes_safe, + to_str_safe, +) +import pytest +import six + +class TestPy3Helpers(object): + def test_iter_single_bytes_bytes(self): + i = iter_single_bytes(b"1234") + assert next(i) == b'1' + assert next(i) == b'2' + assert next(i) == b'3' + assert next(i) == b'4' + + def test_to_bytes_safe(self): + if PY3: + assert to_bytes_safe(b"hello") == b"hello" + assert to_bytes_safe("string") == b"string" + else: + assert to_bytes_safe(b"hello") == b"hello" + assert to_bytes_safe("string") == b"string" + assert to_bytes_safe(u"unicode") == b"unicode" + + def test_to_str_safe(self): + if PY3: + assert to_str_safe(b"bytes") == "bytes" + assert to_str_safe("string") == "string" + assert to_str_safe('System Administrator\u2019s Mouse') == 'System Administrator\u2019s Mouse' + else: + assert to_str_safe(b"bytes") == "bytes" + assert to_str_safe("string") == "string" + assert to_str_safe(u"string") == "string" + assert to_str_safe(u'System Administrator\u2019s Mouse') == 'System Administrator\xe2\x80\x99s Mouse' diff --git a/pyocd/utility/py3_helpers.py b/pyocd/utility/py3_helpers.py index ad053feae..7d6874974 100644 --- a/pyocd/utility/py3_helpers.py +++ b/pyocd/utility/py3_helpers.py @@ -34,13 +34,13 @@ if PY3: def to_bytes_safe(v): if type(v) is str: - return v.encode('latin-1') + return v.encode('utf-8') else: return v else: def to_bytes_safe(v): if type(v) is unicode: - return v.encode('latin-1') + return v.encode('utf-8') else: return v @@ -52,11 +52,11 @@ def to_str_safe(v): if type(v) is str: return v else: - return v.decode('latin-1') + return v.decode('utf-8') else: def to_str_safe(v): if type(v) is unicode: - return v.decode('latin-1') + return v.encode('utf-8') else: return v