diff options
Diffstat (limited to 'lib/python/qmk')
| -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 | 13 | ||||
| -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 | 
9 files changed, 150 insertions, 95 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..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..e2c76e4465 100644 --- a/lib/python/qmk/cli/lint.py +++ b/lib/python/qmk/cli/lint.py @@ -26,7 +26,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 +171,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      """ @@ -255,6 +263,9 @@ 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 +      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: | 
