summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/python/qmk/cli/list/__init__.py1
-rw-r--r--lib/python/qmk/cli/list/keymaps.py22
-rw-r--r--lib/python/qmk/keymap.py38
-rw-r--r--lib/python/qmk/makefile.py83
-rw-r--r--lib/python/qmk/tests/test_cli_commands.py12
5 files changed, 156 insertions, 0 deletions
diff --git a/lib/python/qmk/cli/list/__init__.py b/lib/python/qmk/cli/list/__init__.py
index c36ba69548..d83cd20b5b 100644
--- a/lib/python/qmk/cli/list/__init__.py
+++ b/lib/python/qmk/cli/list/__init__.py
@@ -1 +1,2 @@
from . import keyboards
+from . import keymaps
diff --git a/lib/python/qmk/cli/list/keymaps.py b/lib/python/qmk/cli/list/keymaps.py
new file mode 100644
index 0000000000..d199d29bc3
--- /dev/null
+++ b/lib/python/qmk/cli/list/keymaps.py
@@ -0,0 +1,22 @@
+"""List the keymaps for a specific keyboard
+"""
+from milc import cli
+import qmk.keymap
+from qmk.errors import NoSuchKeyboardError
+
+
+@cli.argument("-kb", "--keyboard", help="Specify keyboard name. Example: 1upkeyboards/1up60hse")
+@cli.subcommand("List the keymaps for a specific keyboard")
+def list_keymaps(cli):
+ """List the keymaps for a specific keyboard
+ """
+ try:
+ for name in qmk.keymap.list_keymaps(cli.config.list_keymaps.keyboard):
+ # We echo instead of cli.log.info to allow easier piping of this output
+ cli.echo('%s', name)
+ except NoSuchKeyboardError as e:
+ cli.echo("{fg_red}%s: %s", cli.config.list_keymaps.keyboard, e.message)
+ except (FileNotFoundError, PermissionError) as e:
+ cli.echo("{fg_red}%s: %s", cli.config.list_keymaps.keyboard, e)
+ except TypeError:
+ cli.echo("{fg_red}Something went wrong. Did you specify a keyboard?")
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
index f4a2893f38..15a91a276b 100644
--- a/lib/python/qmk/keymap.py
+++ b/lib/python/qmk/keymap.py
@@ -1,8 +1,10 @@
"""Functions that help you work with QMK keymaps.
"""
import os
+from pathlib import Path
import qmk.path
+import qmk.makefile
# The `keymap.c` template to use when a keyboard doesn't have its own
DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
@@ -94,3 +96,39 @@ def write(keyboard, keymap, layout, layers):
keymap_fd.write(keymap_c)
return keymap_file
+
+
+def list_keymaps(keyboard_name):
+ """ List the available keymaps for a keyboard.
+
+ Args:
+ keyboard_name: the keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3
+
+ Returns:
+ a set with the names of the available keymaps
+ """
+ # parse all the rules.mk files for the keyboard
+ rules_mk = qmk.makefile.get_rules_mk(keyboard_name)
+ names = set()
+
+ if rules_mk:
+ # qmk_firmware/keyboards
+ keyboards_dir = Path.cwd() / "keyboards"
+ # path to the keyboard's directory
+ kb_path = keyboards_dir / keyboard_name
+ # walk up the directory tree until keyboards_dir
+ # and collect all directories' name with keymap.c file in it
+ while kb_path != keyboards_dir:
+ keymaps_dir = kb_path / "keymaps"
+ if keymaps_dir.exists():
+ names = names.union([keymap for keymap in os.listdir(str(keymaps_dir)) if (keymaps_dir / keymap / "keymap.c").is_file()])
+ kb_path = kb_path.parent
+
+ # if community layouts are supported, get them
+ if "LAYOUTS" in rules_mk:
+ for layout in rules_mk["LAYOUTS"].split():
+ cl_path = Path.cwd() / "layouts" / "community" / layout
+ if cl_path.exists():
+ names = names.union([keymap for keymap in os.listdir(str(cl_path)) if (cl_path / keymap / "keymap.c").is_file()])
+
+ return sorted(names)
diff --git a/lib/python/qmk/makefile.py b/lib/python/qmk/makefile.py
new file mode 100644
index 0000000000..8645056d2d
--- /dev/null
+++ b/lib/python/qmk/makefile.py
@@ -0,0 +1,83 @@
+""" Functions for working with Makefiles
+"""
+from pathlib import Path
+
+from qmk.errors import NoSuchKeyboardError
+
+
+def parse_rules_mk_file(file, rules_mk=None):
+ """Turn a rules.mk file into a dictionary.
+
+ Args:
+ file: path to the rules.mk file
+ rules_mk: already parsed rules.mk the new file should be merged with
+
+ Returns:
+ a dictionary with the file's content
+ """
+ if not rules_mk:
+ rules_mk = {}
+
+ file = Path(file)
+ if file.exists():
+ rules_mk_lines = file.read_text().split("\n")
+
+ for line in rules_mk_lines:
+ # Filter out comments
+ if line.strip().startswith("#"):
+ continue
+
+ # Strip in-line comments
+ if '#' in line:
+ line = line[:line.index('#')].strip()
+
+ if '=' in line:
+ # Append
+ if '+=' in line:
+ key, value = line.split('+=', 1)
+ if key.strip() not in rules_mk:
+ rules_mk[key.strip()] = value.strip()
+ else:
+ rules_mk[key.strip()] += ' ' + value.strip()
+ # Set if absent
+ elif "?=" in line:
+ key, value = line.split('?=', 1)
+ if key.strip() not in rules_mk:
+ rules_mk[key.strip()] = value.strip()
+ else:
+ if ":=" in line:
+ line.replace(":", "")
+ key, value = line.split('=', 1)
+ rules_mk[key.strip()] = value.strip()
+
+ return rules_mk
+
+
+def get_rules_mk(keyboard):
+ """ Get a rules.mk for a keyboard
+
+ Args:
+ keyboard: name of the keyboard
+
+ Raises:
+ NoSuchKeyboardError: when the keyboard does not exists
+
+ Returns:
+ a dictionary with the content of the rules.mk file
+ """
+ # Start with qmk_firmware/keyboards
+ kb_path = Path.cwd() / "keyboards"
+ # walk down the directory tree
+ # and collect all rules.mk files
+ kb_dir = kb_path / keyboard
+ if kb_dir.exists():
+ rules_mk = dict()
+ for directory in Path(keyboard).parts:
+ kb_path = kb_path / directory
+ rules_mk_path = kb_path / "rules.mk"
+ if rules_mk_path.exists():
+ rules_mk = parse_rules_mk_file(rules_mk_path, rules_mk)
+ else:
+ raise NoSuchKeyboardError("The requested keyboard and/or revision does not exist.")
+
+ return rules_mk
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py
index f1a92d9a31..bb77952faf 100644
--- a/lib/python/qmk/tests/test_cli_commands.py
+++ b/lib/python/qmk/tests/test_cli_commands.py
@@ -54,3 +54,15 @@ def test_list_keyboards():
# check to see if a known keyboard is returned
# this will fail if handwired/onekey/pytest is removed
assert 'handwired/onekey/pytest' in result.stdout
+
+
+def test_list_keymaps():
+ result = check_subcommand("list-keymaps", "-kb", "handwired/onekey/pytest")
+ assert result.returncode == 0
+ assert "default" and "test" in result.stdout
+
+
+def test_list_keymaps_no_keyboard_found():
+ result = check_subcommand("list-keymaps", "-kb", "asdfghjkl")
+ assert result.returncode == 0
+ assert "does not exist" in result.stdout