aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
m---------lib/pico-sdk0
-rw-r--r--lib/python/qmk/cli/__init__.py3
-rw-r--r--lib/python/qmk/cli/generate/community_modules.py65
-rw-r--r--lib/python/qmk/cli/generate/keycodes_tests.py39
-rwxr-xr-xlib/python/qmk/cli/generate/rules_mk.py34
-rw-r--r--lib/python/qmk/cli/lint.py44
-rwxr-xr-xlib/python/qmk/cli/new/keymap.py56
-rw-r--r--lib/python/qmk/info.py16
-rwxr-xr-xlib/python/qmk/json_encoders.py8
-rw-r--r--lib/python/qmk/keymap.py11
10 files changed, 181 insertions, 95 deletions
diff --git a/lib/pico-sdk b/lib/pico-sdk
-Subproject a3398d8d3a772f37fef44a74743a1de69770e9c
+Subproject d0c5cac430cc955b65efa0e899748853d9a8092
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py
index 3f2ba9ce3c..26905ec134 100644
--- a/lib/python/qmk/cli/__init__.py
+++ b/lib/python/qmk/cli/__init__.py
@@ -58,7 +58,6 @@ subcommands = [
'qmk.cli.generate.keyboard_c',
'qmk.cli.generate.keyboard_h',
'qmk.cli.generate.keycodes',
- 'qmk.cli.generate.keycodes_tests',
'qmk.cli.generate.keymap_h',
'qmk.cli.generate.make_dependencies',
'qmk.cli.generate.rgb_breathe_table',
@@ -215,7 +214,7 @@ if sys.version_info[0] != 3 or sys.version_info[1] < 9:
milc_version = __VERSION__.split('.')
-if int(milc_version[0]) < 2 and int(milc_version[1]) < 4:
+if int(milc_version[0]) < 2 and int(milc_version[1]) < 9:
requirements = Path('requirements.txt').resolve()
_eprint(f'Your MILC library is too old! Please upgrade: python3 -m pip install -U -r {str(requirements)}')
diff --git a/lib/python/qmk/cli/generate/community_modules.py b/lib/python/qmk/cli/generate/community_modules.py
index 23678a2fb5..e12daccf1c 100644
--- a/lib/python/qmk/cli/generate/community_modules.py
+++ b/lib/python/qmk/cli/generate/community_modules.py
@@ -8,7 +8,7 @@ import qmk.path
from qmk.info import get_modules
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.commands import dump_lines
-from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
+from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE
from qmk.community_modules import module_api_list, load_module_jsons, find_module_path
@@ -121,6 +121,69 @@ def _render_core_implementation(api, modules):
return lines
+def _generate_features_rules(features_dict):
+ lines = []
+ for feature, enabled in features_dict.items():
+ feature = feature.upper()
+ enabled = 'yes' if enabled else 'no'
+ lines.append(f'{feature}_ENABLE={enabled}')
+ return lines
+
+
+def _generate_modules_rules(keyboard, filename):
+ lines = []
+ modules = get_modules(keyboard, filename)
+ if len(modules) > 0:
+ lines.append('')
+ lines.append('OPT_DEFS += -DCOMMUNITY_MODULES_ENABLE=TRUE')
+ for module in modules:
+ module_path = qmk.path.unix_style_path(find_module_path(module))
+ if not module_path:
+ raise FileNotFoundError(f"Module '{module}' not found.")
+ lines.append('')
+ lines.append(f'COMMUNITY_MODULES += {module_path.name}') # use module_path here instead of module as it may be a subdirectory
+ lines.append(f'OPT_DEFS += -DCOMMUNITY_MODULE_{module_path.name.upper()}_ENABLE=TRUE')
+ lines.append(f'COMMUNITY_MODULE_PATHS += {module_path}')
+ lines.append(f'VPATH += {module_path}')
+ lines.append(f'SRC += $(wildcard {module_path}/{module_path.name}.c)')
+ lines.append(f'MODULE_NAME_{module_path.name.upper()} := {module_path.name}')
+ lines.append(f'MODULE_PATH_{module_path.name.upper()} := {module_path}')
+ lines.append(f'-include {module_path}/rules.mk')
+
+ module_jsons = load_module_jsons(modules)
+ for module_json in module_jsons:
+ if 'features' in module_json:
+ lines.append('')
+ lines.append(f'# Module: {module_json["module_name"]}')
+ lines.extend(_generate_features_rules(module_json['features']))
+ return lines
+
+
[email protected]('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
[email protected]('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
[email protected]('-e', '--escape', arg_only=True, action='store_true', help="Escape spaces in quiet mode")
[email protected]('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate rules.mk for.')
[email protected]('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.')
[email protected]('Creates a community_modules_rules_mk from a keymap.json file.')
+def generate_community_modules_rules_mk(cli):
+
+ rules_mk_lines = [GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE]
+
+ rules_mk_lines.extend(_generate_modules_rules(cli.args.keyboard, cli.args.filename))
+
+ # Show the results
+ dump_lines(cli.args.output, rules_mk_lines)
+
+ if cli.args.output:
+ if cli.args.quiet:
+ if cli.args.escape:
+ print(cli.args.output.as_posix().replace(' ', '\\ '))
+ else:
+ print(cli.args.output)
+ else:
+ cli.log.info('Wrote rules.mk to %s.', cli.args.output)
+
+
@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate community_modules.h for.')
diff --git a/lib/python/qmk/cli/generate/keycodes_tests.py b/lib/python/qmk/cli/generate/keycodes_tests.py
deleted file mode 100644
index 453b4693a7..0000000000
--- a/lib/python/qmk/cli/generate/keycodes_tests.py
+++ /dev/null
@@ -1,39 +0,0 @@
-"""Used by the make system to generate a keycode lookup table from keycodes_{version}.json
-"""
-from milc import cli
-
-from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
-from qmk.commands import dump_lines
-from qmk.path import normpath
-from qmk.keycodes import load_spec
-
-
-def _generate_defines(lines, keycodes):
- lines.append('')
- lines.append('std::map<uint16_t, std::string> KEYCODE_ID_TABLE = {')
- for key, value in keycodes["keycodes"].items():
- lines.append(f' {{{value.get("key")}, "{value.get("key")}"}},')
- lines.append('};')
-
-
[email protected]('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.')
[email protected]('-o', '--output', arg_only=True, type=normpath, help='File to write to')
[email protected]('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
[email protected]('Used by the make system to generate a keycode lookup table from keycodes_{version}.json', hidden=True)
-def generate_keycodes_tests(cli):
- """Generates a keycode to identifier lookup table for unit test output.
- """
-
- # Build the keycodes.h file.
- keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '// clang-format off']
- keycodes_h_lines.append('extern "C" {\n#include <keycode.h>\n}')
- keycodes_h_lines.append('#include <map>')
- keycodes_h_lines.append('#include <string>')
- keycodes_h_lines.append('#include <cstdint>')
-
- keycodes = load_spec(cli.args.version)
-
- _generate_defines(keycodes_h_lines, keycodes)
-
- # Show the results
- dump_lines(cli.args.output, keycodes_h_lines, cli.args.quiet)
diff --git a/lib/python/qmk/cli/generate/rules_mk.py b/lib/python/qmk/cli/generate/rules_mk.py
index ef4101d77f..358a22fd1d 100755
--- a/lib/python/qmk/cli/generate/rules_mk.py
+++ b/lib/python/qmk/cli/generate/rules_mk.py
@@ -6,13 +6,12 @@ from dotty_dict import dotty
from argcomplete.completers import FilesCompleter
from milc import cli
-from qmk.info import info_json, get_modules
+from qmk.info import info_json
from qmk.json_schema import json_load
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.commands import dump_lines, parse_configurator_json
-from qmk.path import normpath, unix_style_path, FileType
+from qmk.path import normpath, FileType
from qmk.constants import GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE
-from qmk.community_modules import find_module_path, load_module_jsons
def generate_rule(rules_key, rules_value):
@@ -56,33 +55,6 @@ def generate_features_rules(features_dict):
return lines
-def generate_modules_rules(keyboard, filename):
- lines = []
- modules = get_modules(keyboard, filename)
- if len(modules) > 0:
- lines.append('')
- lines.append('OPT_DEFS += -DCOMMUNITY_MODULES_ENABLE=TRUE')
- for module in modules:
- module_path = unix_style_path(find_module_path(module))
- if not module_path:
- raise FileNotFoundError(f"Module '{module}' not found.")
- lines.append('')
- lines.append(f'COMMUNITY_MODULES += {module_path.name}') # use module_path here instead of module as it may be a subdirectory
- lines.append(f'OPT_DEFS += -DCOMMUNITY_MODULE_{module_path.name.upper()}_ENABLE=TRUE')
- lines.append(f'COMMUNITY_MODULE_PATHS += {module_path}')
- lines.append(f'VPATH += {module_path}')
- lines.append(f'SRC += $(wildcard {module_path}/{module_path.name}.c)')
- lines.append(f'-include {module_path}/rules.mk')
-
- module_jsons = load_module_jsons(modules)
- for module_json in module_jsons:
- if 'features' in module_json:
- lines.append('')
- lines.append(f'# Module: {module_json["module_name"]}')
- lines.extend(generate_features_rules(module_json['features']))
- return lines
-
-
@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.')
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@@ -133,8 +105,6 @@ def generate_rules_mk(cli):
if converter:
rules_mk_lines.append(generate_rule('CONVERT_TO', converter))
- rules_mk_lines.extend(generate_modules_rules(cli.args.keyboard, cli.args.filename))
-
# Show the results
dump_lines(cli.args.output, rules_mk_lines)
diff --git a/lib/python/qmk/cli/lint.py b/lib/python/qmk/cli/lint.py
index bcf905f579..484ddb5bd9 100644
--- a/lib/python/qmk/cli/lint.py
+++ b/lib/python/qmk/cli/lint.py
@@ -1,5 +1,6 @@
"""Command to look over a keyboard/keymap and check for common mistakes.
"""
+from dotty_dict import dotty
from pathlib import Path
from milc import cli
@@ -11,6 +12,7 @@ from qmk.keymap import locate_keymap, list_keymaps
from qmk.path import keyboard
from qmk.git import git_get_ignored_files
from qmk.c_parse import c_source_files, preprocess_c_file
+from qmk.json_schema import json_load
CHIBIOS_CONF_CHECKS = ['chconf.h', 'halconf.h', 'mcuconf.h', 'board.h']
INVALID_KB_FEATURES = set(['encoder_map', 'dip_switch_map', 'combo', 'tap_dance', 'via'])
@@ -26,7 +28,7 @@ def _list_defaultish_keymaps(kb):
defaultish.extend(INVALID_KM_NAMES)
keymaps = set()
- for x in list_keymaps(kb):
+ for x in list_keymaps(kb, include_userspace=False):
if x in defaultish or x.startswith('default'):
keymaps.add(x)
@@ -171,6 +173,14 @@ def _handle_invalid_features(kb, info):
return ok
+def _handle_invalid_config(kb, info):
+ """Check for invalid keyboard level config
+ """
+ if info.get('url') == "":
+ cli.log.warning(f'{kb}: Invalid keyboard level config detected - Optional field "url" should not be empty.')
+ return True
+
+
def _chibios_conf_includenext_check(target):
"""Check the ChibiOS conf.h for the correct inclusion of the next conf.h
"""
@@ -206,6 +216,32 @@ def _rules_mk_assignment_only(rules_mk):
return errors
+def _handle_duplicating_code_defaults(kb, info):
+ def _collect_dotted_output(kb_info_json, prefix=''):
+ """Print the info.json in a plain text format with dot-joined keys.
+ """
+ for key in sorted(kb_info_json):
+ new_prefix = f'{prefix}.{key}' if prefix else key
+
+ if isinstance(kb_info_json[key], dict):
+ yield from _collect_dotted_output(kb_info_json[key], new_prefix)
+ elif isinstance(kb_info_json[key], list):
+ # TODO: handle non primitives?
+ yield (new_prefix, kb_info_json[key])
+ else:
+ yield (new_prefix, kb_info_json[key])
+
+ defaults_map = json_load(Path('data/mappings/info_defaults.hjson'))
+ dotty_info = dotty(info)
+
+ for key, v_default in _collect_dotted_output(defaults_map):
+ v_info = dotty_info.get(key)
+ if v_default == v_info:
+ cli.log.warning(f'{kb}: Option "{key}" duplicates default value of "{v_default}"')
+
+ return True
+
+
def keymap_check(kb, km):
"""Perform the keymap level checks.
"""
@@ -255,6 +291,12 @@ def keyboard_check(kb): # noqa C901
if not _handle_invalid_features(kb, kb_info):
ok = False
+ if not _handle_invalid_config(kb, kb_info):
+ ok = False
+
+ if not _handle_duplicating_code_defaults(kb, kb_info):
+ ok = False
+
invalid_files = git_get_ignored_files(f'keyboards/{kb}/')
for file in invalid_files:
if 'keymap' in file:
diff --git a/lib/python/qmk/cli/new/keymap.py b/lib/python/qmk/cli/new/keymap.py
index 8b7160f5f2..16865ceb00 100755
--- a/lib/python/qmk/cli/new/keymap.py
+++ b/lib/python/qmk/cli/new/keymap.py
@@ -1,10 +1,12 @@
"""This script automates the copying of the default keymap into your own keymap.
"""
import re
+import json
import shutil
+from pathlib import Path
from milc import cli
-from milc.questions import question
+from milc.questions import question, choice
from qmk.constants import HAS_QMK_USERSPACE, QMK_USERSPACE
from qmk.path import is_keyboard, keymaps, keymap
@@ -12,6 +14,34 @@ from qmk.git import git_get_username
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.userspace import UserspaceDefs
+from qmk.json_schema import json_load
+from qmk.json_encoders import KeymapJSONEncoder
+from qmk.info import info_json
+
+
+def _list_available_converters(kb_name):
+ """Search for converters that can be applied to a given keyboard
+ """
+ if not is_keyboard(kb_name):
+ return None
+
+ info = info_json(kb_name)
+ pin_compatible = info.get('pin_compatible')
+ if not pin_compatible:
+ return None
+
+ return sorted(folder.name.split('_to_')[-1] for folder in Path('platforms').glob(f'*/converters/{pin_compatible}_to_*'))
+
+
+def _set_converter(file, converter):
+ """add/overwrite any existing converter specified in keymap.json
+ """
+ json_data = json_load(file) if file.exists() else {}
+
+ json_data['converter'] = converter
+
+ output = json.dumps(json_data, cls=KeymapJSONEncoder, sort_keys=True)
+ file.write_text(output + '\n', encoding='utf-8')
def validate_keymap_name(name):
@@ -37,8 +67,28 @@ Keymap name? """
return question(prompt, default=git_get_username())
+def prompt_converter(kb_name):
+ prompt = """
+{fg_yellow}Configure Development Board{style_reset_all}
+For more information, see:
+https://docs.qmk.fm/feature_converters
+
+Use converter? """
+
+ converters = _list_available_converters(kb_name)
+ if not converters:
+ return None
+
+ choices = ['No (default)', *converters]
+
+ answer = choice(prompt, options=choices, default=0)
+ return None if choices.index(answer) == 0 else answer
+
+
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Specify keyboard name. Example: 1upkeyboards/1up60hse')
@cli.argument('-km', '--keymap', help='Specify the name for the new keymap directory')
[email protected]('--converter', help='Specify the name of a converter to configure')
[email protected]('--skip-converter', arg_only=True, action='store_true', help='Skip converter')
@cli.subcommand('Creates a new keymap for the keyboard of your choosing')
@automagic_keyboard
@automagic_keymap
@@ -51,6 +101,7 @@ def new_keymap(cli):
# ask for user input if keyboard or keymap was not provided in the command line
kb_name = cli.config.new_keymap.keyboard if cli.config.new_keymap.keyboard else prompt_keyboard()
user_name = cli.config.new_keymap.keymap if cli.config.new_keymap.keymap else prompt_user()
+ converter = cli.config.new_keymap.converter if cli.args.skip_converter or cli.config.new_keymap.converter else prompt_converter(kb_name)
# check directories
if not is_keyboard(kb_name):
@@ -77,6 +128,9 @@ def new_keymap(cli):
# create user directory with default keymap files
shutil.copytree(keymap_path_default, keymap_path_new, symlinks=True)
+ if converter:
+ _set_converter(keymap_path_new / 'keymap.json', converter)
+
# end message to user
cli.log.info(f'{{fg_green}}Created a new keymap called {{fg_cyan}}{user_name}{{fg_green}} in: {{fg_cyan}}{keymap_path_new}.{{fg_reset}}')
cli.log.info(f"Compile a firmware with your new keymap by typing: {{fg_yellow}}qmk compile -kb {kb_name} -km {user_name}{{fg_reset}}.")
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py
index 93eba7376a..d95fd3d799 100644
--- a/lib/python/qmk/info.py
+++ b/lib/python/qmk/info.py
@@ -1066,23 +1066,13 @@ def get_modules(keyboard, keymap_filename):
"""
modules = []
+ kb_info_json = info_json(keyboard)
+ modules.extend(kb_info_json.get('modules', []))
+
if keymap_filename:
keymap_json = parse_configurator_json(keymap_filename)
if keymap_json:
- kb = keymap_json.get('keyboard', None)
- if not kb:
- kb = keyboard
-
- if kb:
- kb_info_json = info_json(kb)
- if kb_info_json:
- modules.extend(kb_info_json.get('modules', []))
-
modules.extend(keymap_json.get('modules', []))
- elif keyboard:
- kb_info_json = info_json(keyboard)
- modules.extend(kb_info_json.get('modules', []))
-
return list(dict.fromkeys(modules)) # remove dupes
diff --git a/lib/python/qmk/json_encoders.py b/lib/python/qmk/json_encoders.py
index e83a381d52..e8bcf48996 100755
--- a/lib/python/qmk/json_encoders.py
+++ b/lib/python/qmk/json_encoders.py
@@ -250,12 +250,14 @@ class CommunityModuleJSONEncoder(QMKJSONEncoder):
return '00module_name'
if key == 'maintainer':
return '01maintainer'
+ if key == 'license':
+ return '02license'
if key == 'url':
- return '02url'
+ return '03url'
if key == 'features':
- return '03features'
+ return '04features'
if key == 'keycodes':
- return '04keycodes'
+ return '05keycodes'
elif self.indentation_level == 3: # keycodes
if key == 'key':
return '00key'
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
index 8e36461722..b7138aa4a1 100644
--- a/lib/python/qmk/keymap.py
+++ b/lib/python/qmk/keymap.py
@@ -399,7 +399,7 @@ def is_keymap_target(keyboard, keymap):
return False
-def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=False):
+def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=False, include_userspace=True):
"""List the available keymaps for a keyboard.
Args:
@@ -418,14 +418,19 @@ def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=Fa
fullpath
When set to True the full path of the keymap relative to the `qmk_firmware` root will be provided.
+ include_userspace
+ When set to True, also search userspace for available keymaps
+
Returns:
a sorted list of valid keymap names.
"""
names = set()
+ has_userspace = HAS_QMK_USERSPACE and include_userspace
+
# walk up the directory tree until keyboards_dir
# and collect all directories' name with keymap.c file in it
- for search_dir in [QMK_FIRMWARE, QMK_USERSPACE] if HAS_QMK_USERSPACE else [QMK_FIRMWARE]:
+ for search_dir in [QMK_FIRMWARE, QMK_USERSPACE] if has_userspace else [QMK_FIRMWARE]:
keyboards_dir = search_dir / Path('keyboards')
kb_path = keyboards_dir / keyboard
@@ -443,7 +448,7 @@ def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=Fa
info = info_json(keyboard)
community_parents = list(Path('layouts').glob('*/'))
- if HAS_QMK_USERSPACE and (Path(QMK_USERSPACE) / "layouts").exists():
+ if has_userspace and (Path(QMK_USERSPACE) / "layouts").exists():
community_parents.append(Path(QMK_USERSPACE) / "layouts")
for community_parent in community_parents: