diff options
Diffstat (limited to 'lib')
m--------- | lib/pico-sdk | 0 | ||||
-rw-r--r-- | lib/python/qmk/cli/__init__.py | 3 | ||||
-rw-r--r-- | lib/python/qmk/cli/generate/community_modules.py | 65 | ||||
-rw-r--r-- | lib/python/qmk/cli/generate/keycodes_tests.py | 39 | ||||
-rwxr-xr-x | lib/python/qmk/cli/generate/rules_mk.py | 34 | ||||
-rw-r--r-- | lib/python/qmk/cli/lint.py | 44 | ||||
-rwxr-xr-x | lib/python/qmk/cli/new/keymap.py | 56 | ||||
-rw-r--r-- | lib/python/qmk/info.py | 16 | ||||
-rwxr-xr-x | lib/python/qmk/json_encoders.py | 8 | ||||
-rw-r--r-- | lib/python/qmk/keymap.py | 11 |
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: |