aboutsummaryrefslogtreecommitdiff
path: root/lib/python/qmk/cli
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python/qmk/cli')
-rw-r--r--lib/python/qmk/cli/__init__.py3
-rw-r--r--lib/python/qmk/cli/generate/community_modules.py75
-rw-r--r--[-rwxr-xr-x]lib/python/qmk/cli/generate/compilation_database.py174
-rw-r--r--lib/python/qmk/cli/generate/keycodes.py16
-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.py60
8 files changed, 201 insertions, 244 deletions
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..61e24077d3 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
@@ -76,6 +76,8 @@ def _render_api_implementations(api, module):
lines.append(f'__attribute__((weak)) {api.ret_type} {api.name}_{module_name}_user({api.args}) {{')
if api.ret_type == 'bool':
lines.append(' return true;')
+ elif api.ret_type in ['layer_state_t', 'report_mouse_t']:
+ lines.append(f' return {api.call_params};')
else:
pass
lines.append('}')
@@ -86,6 +88,8 @@ def _render_api_implementations(api, module):
if api.ret_type == 'bool':
lines.append(f' if(!{api.name}_{module_name}_user({api.call_params})) {{ return false; }}')
lines.append(' return true;')
+ elif api.ret_type in ['layer_state_t', 'report_mouse_t']:
+ lines.append(f' return {api.name}_{module_name}_user({api.call_params});')
else:
lines.append(f' {api.name}_{module_name}_user({api.call_params});')
lines.append('}')
@@ -96,6 +100,8 @@ def _render_api_implementations(api, module):
if api.ret_type == 'bool':
lines.append(f' if(!{api.name}_{module_name}_kb({api.call_params})) {{ return false; }}')
lines.append(' return true;')
+ elif api.ret_type in ['layer_state_t', 'report_mouse_t']:
+ lines.append(f' return {api.name}_{module_name}_kb({api.call_params});')
else:
lines.append(f' {api.name}_{module_name}_kb({api.call_params});')
lines.append('}')
@@ -113,14 +119,81 @@ def _render_core_implementation(api, modules):
module_name = Path(module).name
if api.ret_type == 'bool':
lines.append(f' && {api.name}_{module_name}({api.call_params})')
+ elif api.ret_type in ['layer_state_t', 'report_mouse_t']:
+ lines.append(f' {api.call_params} = {api.name}_{module_name}({api.call_params});')
else:
lines.append(f' {api.name}_{module_name}({api.call_params});')
if api.ret_type == 'bool':
lines.append(' ;')
+ elif api.ret_type in ['layer_state_t', 'report_mouse_t']:
+ lines.append(f' return {api.call_params};')
lines.append('}')
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/compilation_database.py b/lib/python/qmk/cli/generate/compilation_database.py
index b9c716bf0c..339b53c2c2 100755..100644
--- a/lib/python/qmk/cli/generate/compilation_database.py
+++ b/lib/python/qmk/cli/generate/compilation_database.py
@@ -1,169 +1,9 @@
-"""Creates a compilation database for the given keyboard build.
-"""
+from milc import cli
-import json
-import os
-import re
-import shlex
-import shutil
-from functools import lru_cache
-from pathlib import Path
-from typing import Dict, Iterator, List, Union
-from milc import cli, MILC
-
-from qmk.commands import find_make
-from qmk.constants import QMK_FIRMWARE
-from qmk.decorators import automagic_keyboard, automagic_keymap
-from qmk.keyboard import keyboard_completer, keyboard_folder
-from qmk.keymap import keymap_completer
-from qmk.build_targets import KeyboardKeymapBuildTarget
-
-
-@lru_cache(maxsize=10)
-def system_libs(binary: str) -> List[Path]:
- """Find the system include directory that the given build tool uses.
- """
- cli.log.debug("searching for system library directory for binary: %s", binary)
-
- # Actually query xxxxxx-gcc to find its include paths.
- if binary.endswith("gcc") or binary.endswith("g++"):
- # (TODO): Remove 'stdin' once 'input' no longer causes issues under MSYS
- result = cli.run([binary, '-E', '-Wp,-v', '-'], capture_output=True, check=True, stdin=None, input='\n')
- paths = []
- for line in result.stderr.splitlines():
- if line.startswith(" "):
- paths.append(Path(line.strip()).resolve())
- return paths
-
- return list(Path(binary).resolve().parent.parent.glob("*/include")) if binary else []
-
-
-@lru_cache(maxsize=10)
-def cpu_defines(binary: str, compiler_args: str) -> List[str]:
- cli.log.debug("gathering definitions for compilation: %s %s", binary, compiler_args)
- if binary.endswith("gcc") or binary.endswith("g++"):
- invocation = [binary, '-dM', '-E']
- if binary.endswith("gcc"):
- invocation.extend(['-x', 'c'])
- elif binary.endswith("g++"):
- invocation.extend(['-x', 'c++'])
- compiler_args = shlex.split(compiler_args)
- invocation.extend(compiler_args)
- invocation.append('-')
- result = cli.run(invocation, capture_output=True, check=True, stdin=None, input='\n')
- define_args = []
- for line in result.stdout.splitlines():
- line_args = line.split(' ', 2)
- if len(line_args) == 3 and line_args[0] == '#define':
- define_args.append(f'-D{line_args[1]}={line_args[2]}')
- elif len(line_args) == 2 and line_args[0] == '#define':
- define_args.append(f'-D{line_args[1]}')
- return list(sorted(set(define_args)))
- return []
-
-
-file_re = re.compile(r'printf "Compiling: ([^"]+)')
-cmd_re = re.compile(r'LOG=\$\((.+?)&&')
-
-
-def parse_make_n(f: Iterator[str]) -> List[Dict[str, str]]:
- """parse the output of `make -n <target>`
-
- This function makes many assumptions about the format of your build log.
- This happens to work right now for qmk.
- """
-
- state = 'start'
- this_file = None
- records = []
- for line in f:
- if state == 'start':
- m = file_re.search(line)
- if m:
- this_file = m.group(1)
- state = 'cmd'
-
- if state == 'cmd':
- assert this_file
- m = cmd_re.search(line)
- if m:
- # we have a hit!
- this_cmd = m.group(1)
- args = shlex.split(this_cmd)
- binary = shutil.which(args[0])
- compiler_args = set(filter(lambda x: x.startswith('-m') or x.startswith('-f'), args))
- for s in system_libs(binary):
- args += ['-isystem', '%s' % s]
- args.extend(cpu_defines(binary, ' '.join(shlex.quote(s) for s in compiler_args)))
- new_cmd = ' '.join(shlex.quote(s) for s in args)
- records.append({"directory": str(QMK_FIRMWARE.resolve()), "command": new_cmd, "file": this_file})
- state = 'start'
-
- return records
-
-
-def write_compilation_database(keyboard: str = None, keymap: str = None, output_path: Path = QMK_FIRMWARE / 'compile_commands.json', skip_clean: bool = False, command: List[str] = None, **env_vars) -> bool:
- # Generate the make command for a specific keyboard/keymap.
- if not command:
- from qmk.build_targets import KeyboardKeymapBuildTarget # Lazy load due to circular references
- target = KeyboardKeymapBuildTarget(keyboard, keymap)
- command = target.compile_command(dry_run=True, **env_vars)
-
- if not command:
- cli.log.error('You must supply both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
- cli.echo('usage: qmk generate-compilation-database [-kb KEYBOARD] [-km KEYMAP]')
- return False
-
- # remove any environment variable overrides which could trip us up
- env = os.environ.copy()
- env.pop("MAKEFLAGS", None)
-
- # re-use same executable as the main make invocation (might be gmake)
- if not skip_clean:
- clean_command = [find_make(), "clean"]
- cli.log.info('Making clean with {fg_cyan}%s', ' '.join(clean_command))
- cli.run(clean_command, capture_output=False, check=True, env=env)
-
- cli.log.info('Gathering build instructions from {fg_cyan}%s', ' '.join(command))
-
- result = cli.run(command, capture_output=True, check=True, env=env)
- db = parse_make_n(result.stdout.splitlines())
- if not db:
- cli.log.error("Failed to parse output from make output:\n%s", result.stdout)
- return False
-
- cli.log.info("Found %s compile commands", len(db))
-
- cli.log.info(f"Writing build database to {output_path}")
- output_path.write_text(json.dumps(db, indent=4))
-
- return True
-
-
[email protected]('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard\'s name')
[email protected]('-km', '--keymap', completer=keymap_completer, help='The keymap\'s name')
[email protected]('Create a compilation database.')
-@automagic_keyboard
-@automagic_keymap
-def generate_compilation_database(cli: MILC) -> Union[bool, int]:
- """Creates a compilation database for the given keyboard build.
-
- Does a make clean, then a make -n for this target and uses the dry-run output to create
- a compilation database (compile_commands.json). This file can help some IDEs and
- IDE-like editors work better. For more information about this:
-
- https://clang.llvm.org/docs/JSONCompilationDatabase.html
- """
- # check both config domains: the magic decorator fills in `generate_compilation_database` but the user is
- # more likely to have set `compile` in their config file.
- current_keyboard = cli.config.generate_compilation_database.keyboard or cli.config.user.keyboard
- current_keymap = cli.config.generate_compilation_database.keymap or cli.config.user.keymap
-
- if not current_keyboard:
- cli.log.error('Could not determine keyboard!')
- elif not current_keymap:
- cli.log.error('Could not determine keymap!')
-
- target = KeyboardKeymapBuildTarget(current_keyboard, current_keymap)
- return target.generate_compilation_database()
[email protected]('-kb', '--keyboard', help='[unused] The keyboard\'s name')
[email protected]('-km', '--keymap', help='[unused] The keymap\'s name')
[email protected]('[deprecated] Create a compilation database.')
+def generate_compilation_database(cli):
+ cli.log.error('This command is deprecated and has effectively been removed. Please use the `--compiledb` flag with `qmk compile` instead.')
+ return False
diff --git a/lib/python/qmk/cli/generate/keycodes.py b/lib/python/qmk/cli/generate/keycodes.py
index 719fced5d5..d686935fa8 100644
--- a/lib/python/qmk/cli/generate/keycodes.py
+++ b/lib/python/qmk/cli/generate/keycodes.py
@@ -125,6 +125,20 @@ def _generate_aliases(lines, keycodes):
lines.append(f'#define {alias} {value.get("key")}')
+def _generate_version(lines, keycodes, prefix=''):
+ version = keycodes['version']
+ major, minor, patch = map(int, version.split('.'))
+
+ bcd = f'0x{major:02d}{minor:02d}{patch:04d}'
+
+ lines.append('')
+ lines.append(f'#define QMK_{prefix}KEYCODES_VERSION "{version}"')
+ lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_BCD {bcd}')
+ lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_MAJOR {major}')
+ lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_MINOR {minor}')
+ lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_PATCH {patch}')
+
+
@cli.argument('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.')
@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")
@@ -138,6 +152,7 @@ def generate_keycodes(cli):
keycodes = load_spec(cli.args.version)
+ _generate_version(keycodes_h_lines, keycodes)
_generate_ranges(keycodes_h_lines, keycodes)
_generate_defines(keycodes_h_lines, keycodes)
_generate_helpers(keycodes_h_lines, keycodes)
@@ -160,6 +175,7 @@ def generate_keycode_extras(cli):
keycodes = load_spec(cli.args.version, cli.args.lang)
+ _generate_version(keycodes_h_lines, keycodes, f'{cli.args.lang.upper()}_')
_generate_aliases(keycodes_h_lines, keycodes)
# Show the results
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..20a0c0198c 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,9 +101,12 @@ 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):
+ try:
+ kb_name = keyboard_folder(kb_name)
+ except ValueError:
cli.log.error(f'Keyboard {{fg_cyan}}{kb_name}{{fg_reset}} does not exist! Please choose a valid name.')
return False
@@ -77,6 +130,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}}.")