summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErovia <erovia@users.noreply.github.com>2019-10-08 21:50:21 +0200
committerDrashna Jael're <drashna@live.com>2020-03-26 00:42:11 -0700
commit4c3335b00bbcb4fa96056babcae6b10b4ab877a1 (patch)
treee57629841445b747dfa95cdb2e40325c1817cc7d
parent2b0c1a766176eaddb2af37bb24b1f39003d6afb7 (diff)
CLI: add support for list_keymaps
List all the available keymaps for a given keyboard Add bs4 to requirements.txt UnicodeDammit is needed from bs4 for reading files. Major update to work better with revisions Find the community keymaps supported by each revision. Get all buildable keymaps for each revision The command now return all keymaps that's buildable for a keyboard/revision. If the base directory of a keyboard does not contain a 'rules.mk' file, nothing is returned. If the base directory contains a 'keymaps' directory, those keycaps will be returned for every revision. Fix help message. Try to figure out revision, drop -rv/--revision argument Fix output format Another major refactoring, add documentation Move all useful functions to the qmk module and use the cli subcommand as a wrapper around it. Add both inline comments and documentation. Add test for list_keymaps Fix regex for parsing rules.mk files I don't know why it couldn't put it together before... ¯\_(ツ)_/¯ Drop bs4 dependency, update docs, minor improvements Return only the unique keymaps Fix merging community and base keymaps Major rework, no regex/globbing, more walking Instead of using regexes and globbing to find the rules.mk and keymap.c files, walk the directory tree to find them. Also, do away with the concept of revision. Fix commandline parsing and flake8 findings, rebase Fixed commandline and config parsing. Thx @xplusplus. Rebased on master and fixed merge conflicts. Code cleanup, use pathlib, use pytest keyboard Clean up checks and logics that are unnecessary due to MILC updates. Use pathlib instead of os.path for readability. Use the 'pytest' keyboard for the tests. Add community layout for 'handwired/onekey/pytest' so we can test community layouts. Pathlib-ify qmk.keymap.list_keymaps() fix list_keymaps for python 3.5
-rw-r--r--layouts/community/ortho_1x1/layout.json1
-rw-r--r--layouts/community/ortho_1x1/test/keymap.c12
-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
7 files changed, 169 insertions, 0 deletions
diff --git a/layouts/community/ortho_1x1/layout.json b/layouts/community/ortho_1x1/layout.json
new file mode 100644
index 0000000000..66a1e18560
--- /dev/null
+++ b/layouts/community/ortho_1x1/layout.json
@@ -0,0 +1 @@
+[""]
diff --git a/layouts/community/ortho_1x1/test/keymap.c b/layouts/community/ortho_1x1/test/keymap.c
new file mode 100644
index 0000000000..6a186669b0
--- /dev/null
+++ b/layouts/community/ortho_1x1/test/keymap.c
@@ -0,0 +1,12 @@
+#include QMK_KEYBOARD_H
+
+/* This keyboard/layout is used to test community layout discovery/compilation. */
+
+#define _DEFAULT 0
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+
+[_DEFAULT] = LAYOUT (
+ KC_B
+),
+};
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