diff options
Diffstat (limited to 'lib/python')
| -rw-r--r-- | lib/python/qmk/cli/__init__.py | 1 | ||||
| -rw-r--r-- | lib/python/qmk/cli/find.py | 23 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/mass_compile.py | 91 | ||||
| -rw-r--r-- | lib/python/qmk/search.py | 99 | 
4 files changed, 125 insertions, 89 deletions
| diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 778eccada8..de7b0476a0 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -39,6 +39,7 @@ subcommands = [      'qmk.cli.compile',      'qmk.cli.docs',      'qmk.cli.doctor', +    'qmk.cli.find',      'qmk.cli.flash',      'qmk.cli.format.c',      'qmk.cli.format.json', diff --git a/lib/python/qmk/cli/find.py b/lib/python/qmk/cli/find.py new file mode 100644 index 0000000000..b6f74380ab --- /dev/null +++ b/lib/python/qmk/cli/find.py @@ -0,0 +1,23 @@ +"""Command to search through all keyboards and keymaps for a given search criteria. +""" +from milc import cli +from qmk.search import search_keymap_targets + + +    '-f', +    '--filter', +    arg_only=True, +    action='append', +    default=[], +    help=  # noqa: `format-python` and `pytest` don't agree here. +    "Filter the list of keyboards based on the supplied value in rules.mk. Matches info.json structure, and accepts the formats 'features.rgblight=true' or 'exists(matrix_pins.direct)'. May be passed multiple times, all filters need to match. Value may include wildcards such as '*' and '?'."  # noqa: `format-python` and `pytest` don't agree here. +) [email protected]('-km', '--keymap', type=str, default='default', help="The keymap name to build. Default is 'default'.") [email protected]('Find builds which match supplied search criteria.') +def find(cli): +    """Search through all keyboards and keymaps for a given search criteria. +    """ +    targets = search_keymap_targets(cli.args.keymap, cli.args.filter) +    for target in targets: +        print(f'{target[0]}:{target[1]}') diff --git a/lib/python/qmk/cli/mass_compile.py b/lib/python/qmk/cli/mass_compile.py index 810350b954..941e6aa411 100755 --- a/lib/python/qmk/cli/mass_compile.py +++ b/lib/python/qmk/cli/mass_compile.py @@ -2,52 +2,14 @@  This will compile everything in parallel, for testing purposes.  """ -import fnmatch -import logging -import multiprocessing  import os -import re  from pathlib import Path  from subprocess import DEVNULL -from dotty_dict import dotty  from milc import cli  from qmk.constants import QMK_FIRMWARE  from qmk.commands import _find_make, get_make_parallel_args -from qmk.info import keymap_json -import qmk.keyboard -import qmk.keymap - - -def _set_log_level(level): -    cli.acquire_lock() -    old = cli.log_level -    cli.log_level = level -    cli.log.setLevel(level) -    logging.root.setLevel(level) -    cli.release_lock() -    return old - - -def _all_keymaps(keyboard): -    old = _set_log_level(logging.CRITICAL) -    keymaps = qmk.keymap.list_keymaps(keyboard) -    _set_log_level(old) -    return (keyboard, keymaps) - - -def _keymap_exists(keyboard, keymap): -    old = _set_log_level(logging.CRITICAL) -    ret = keyboard if qmk.keymap.locate_keymap(keyboard, keymap) is not None else None -    _set_log_level(old) -    return ret - - -def _load_keymap_info(keyboard, keymap): -    old = _set_log_level(logging.CRITICAL) -    ret = (keyboard, keymap, keymap_json(keyboard, keymap)) -    _set_log_level(old) -    return ret +from qmk.search import search_keymap_targets  @cli.argument('-t', '--no-temp', arg_only=True, action='store_true', help="Remove temporary files during build.") @@ -75,56 +37,7 @@ def mass_compile(cli):      builddir = Path(QMK_FIRMWARE) / '.build'      makefile = builddir / 'parallel_kb_builds.mk' -    targets = [] - -    with multiprocessing.Pool() as pool: -        cli.log.info(f'Retrieving list of keyboards with keymap "{cli.args.keymap}"...') -        target_list = [] -        if cli.args.keymap == 'all': -            kb_to_kms = pool.map(_all_keymaps, qmk.keyboard.list_keyboards()) -            for targets in kb_to_kms: -                keyboard = targets[0] -                keymaps = targets[1] -                target_list.extend([(keyboard, keymap) for keymap in keymaps]) -        else: -            target_list = [(kb, cli.args.keymap) for kb in filter(lambda kb: kb is not None, pool.starmap(_keymap_exists, [(kb, cli.args.keymap) for kb in qmk.keyboard.list_keyboards()]))] - -        if len(cli.args.filter) == 0: -            targets = target_list -        else: -            cli.log.info('Parsing data for all matching keyboard/keymap combinations...') -            valid_keymaps = [(e[0], e[1], dotty(e[2])) for e in pool.starmap(_load_keymap_info, target_list)] - -            equals_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*=\s*(?P<value>[^#]+)$') -            exists_re = re.compile(r'^exists\((?P<key>[a-zA-Z0-9_\.]+)\)$') -            for filter_txt in cli.args.filter: -                f = equals_re.match(filter_txt) -                if f is not None: -                    key = f.group('key') -                    value = f.group('value') -                    cli.log.info(f'Filtering on condition ("{key}" == "{value}")...') - -                    def _make_filter(k, v): -                        expr = fnmatch.translate(v) -                        rule = re.compile(f'^{expr}$', re.IGNORECASE) - -                        def f(e): -                            lhs = e[2].get(k) -                            lhs = str(False if lhs is None else lhs) -                            return rule.search(lhs) is not None - -                        return f - -                    valid_keymaps = filter(_make_filter(key, value), valid_keymaps) - -                f = exists_re.match(filter_txt) -                if f is not None: -                    key = f.group('key') -                    cli.log.info(f'Filtering on condition (exists: "{key}")...') -                    valid_keymaps = filter(lambda e: e[2].get(key) is not None, valid_keymaps) - -            targets = [(e[0], e[1]) for e in valid_keymaps] - +    targets = search_keymap_targets(cli.args.keymap, cli.args.filter)      if len(targets) == 0:          return diff --git a/lib/python/qmk/search.py b/lib/python/qmk/search.py new file mode 100644 index 0000000000..af48900e6b --- /dev/null +++ b/lib/python/qmk/search.py @@ -0,0 +1,99 @@ +"""Functions for searching through QMK keyboards and keymaps. +""" +import contextlib +import fnmatch +import logging +import multiprocessing +import re +from dotty_dict import dotty +from milc import cli + +from qmk.info import keymap_json +import qmk.keyboard +import qmk.keymap + + +def _set_log_level(level): +    cli.acquire_lock() +    old = cli.log_level +    cli.log_level = level +    cli.log.setLevel(level) +    logging.root.setLevel(level) +    cli.release_lock() +    return old + + +def ignore_logging(): +    old = _set_log_level(logging.CRITICAL) +    yield +    _set_log_level(old) + + +def _all_keymaps(keyboard): +    with ignore_logging(): +        return (keyboard, qmk.keymap.list_keymaps(keyboard)) + + +def _keymap_exists(keyboard, keymap): +    with ignore_logging(): +        return keyboard if qmk.keymap.locate_keymap(keyboard, keymap) is not None else None + + +def _load_keymap_info(keyboard, keymap): +    with ignore_logging(): +        return (keyboard, keymap, keymap_json(keyboard, keymap)) + + +def search_keymap_targets(keymap='default', filters=[]): +    targets = [] + +    with multiprocessing.Pool() as pool: +        cli.log.info(f'Retrieving list of keyboards with keymap "{keymap}"...') +        target_list = [] +        if keymap == 'all': +            kb_to_kms = pool.map(_all_keymaps, qmk.keyboard.list_keyboards()) +            for targets in kb_to_kms: +                keyboard = targets[0] +                keymaps = targets[1] +                target_list.extend([(keyboard, keymap) for keymap in keymaps]) +        else: +            target_list = [(kb, keymap) for kb in filter(lambda kb: kb is not None, pool.starmap(_keymap_exists, [(kb, keymap) for kb in qmk.keyboard.list_keyboards()]))] + +        if len(filters) == 0: +            targets = target_list +        else: +            cli.log.info('Parsing data for all matching keyboard/keymap combinations...') +            valid_keymaps = [(e[0], e[1], dotty(e[2])) for e in pool.starmap(_load_keymap_info, target_list)] + +            equals_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*=\s*(?P<value>[^#]+)$') +            exists_re = re.compile(r'^exists\((?P<key>[a-zA-Z0-9_\.]+)\)$') +            for filter_txt in filters: +                f = equals_re.match(filter_txt) +                if f is not None: +                    key = f.group('key') +                    value = f.group('value') +                    cli.log.info(f'Filtering on condition ("{key}" == "{value}")...') + +                    def _make_filter(k, v): +                        expr = fnmatch.translate(v) +                        rule = re.compile(f'^{expr}$', re.IGNORECASE) + +                        def f(e): +                            lhs = e[2].get(k) +                            lhs = str(False if lhs is None else lhs) +                            return rule.search(lhs) is not None + +                        return f + +                    valid_keymaps = filter(_make_filter(key, value), valid_keymaps) + +                f = exists_re.match(filter_txt) +                if f is not None: +                    key = f.group('key') +                    cli.log.info(f'Filtering on condition (exists: "{key}")...') +                    valid_keymaps = filter(lambda e: e[2].get(key) is not None, valid_keymaps) + +            targets = [(e[0], e[1]) for e in valid_keymaps] + +    return targets | 
