diff options
| author | Joel Challis <[email protected]> | 2022-11-30 04:27:48 +0000 | 
|---|---|---|
| committer | GitHub <[email protected]> | 2022-11-30 04:27:48 +0000 | 
| commit | 17fec52b0fc337ee96dec020e7371ddd5e39cdea (patch) | |
| tree | 5d5052d6cdf9790275a3af96f9c17768c061139b /lib/python | |
| parent | 4a0aa9ada2837762e3b4eab73950962365341cce (diff) | |
| download | qmk_firmware-17fec52b0fc337ee96dec020e7371ddd5e39cdea.tar.gz qmk_firmware-17fec52b0fc337ee96dec020e7371ddd5e39cdea.zip | |
Extend layout lint checks (#19200)
* Extend layout lint checks
* Apply suggestions from code review
Co-authored-by: Ryan <[email protected]>
* Fix function comment
Co-authored-by: Ryan <[email protected]>
Diffstat (limited to 'lib/python')
| -rw-r--r-- | lib/python/qmk/info.py | 99 | 
1 files changed, 46 insertions, 53 deletions
| diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index 1692d43c9c..0260b624af 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -1,6 +1,5 @@  """Functions that help us generate and use info.json files.  """ -from glob import glob  from pathlib import Path  import jsonschema @@ -26,6 +25,50 @@ def _valid_community_layout(layout):      return (Path('layouts/default') / layout).exists() +def _validate(keyboard, info_data): +    """Perform various validation on the provided info.json data +    """ +    # First validate against the jsonschema +    try: +        validate(info_data, 'qmk.api.keyboard.v1') + +    except jsonschema.ValidationError as e: +        json_path = '.'.join([str(p) for p in e.absolute_path]) +        cli.log.error('Invalid API data: %s: %s: %s', keyboard, json_path, e.message) +        exit(1) + +    layouts = info_data.get('layouts', {}) +    layout_aliases = info_data.get('layout_aliases', {}) +    community_layouts = info_data.get('community_layouts', []) + +    # Make sure we have at least one layout +    if len(layouts) == 0: +        _log_error(info_data, 'No LAYOUTs defined! Need at least one layout defined in info.json.') + +    # Providing only LAYOUT_all "because I define my layouts in a 3rd party tool" +    if len(layouts) == 1 and 'LAYOUT_all' in layouts: +        _log_warning(info_data, '"LAYOUT_all" should be "LAYOUT" unless additional layouts are provided.') + +    # Extended layout name checks +    name_fragments = keyboard.split('/') +    for layout in layouts.keys(): +        if any(fragment in layout for fragment in name_fragments): +            _log_warning(info_data, f'Layout "{layout}" should not contain name of keyboard.') + +    # Filter out any non-existing community layouts +    for layout in community_layouts: +        if not _valid_community_layout(layout): +            # Ignore layout from future checks +            info_data['community_layouts'].remove(layout) +            _log_error(info_data, 'Claims to support a community layout that does not exist: %s' % (layout)) + +    # Make sure we supply layout macros for the community layouts we claim to support +    for layout in community_layouts: +        layout_name = 'LAYOUT_' + layout +        if layout_name not in layouts and layout_name not in layout_aliases: +            _log_error(info_data, 'Claims to support community layout %s but no %s() macro found' % (layout, layout_name)) + +  def info_json(keyboard):      """Generate the info.json data for a specific keyboard.      """ @@ -72,34 +115,8 @@ def info_json(keyboard):      # Merge in data from <keyboard.c>      info_data = _extract_led_config(info_data, str(keyboard)) -    # Validate against the jsonschema -    try: -        validate(info_data, 'qmk.api.keyboard.v1') - -    except jsonschema.ValidationError as e: -        json_path = '.'.join([str(p) for p in e.absolute_path]) -        cli.log.error('Invalid API data: %s: %s: %s', keyboard, json_path, e.message) -        exit(1) - -    # Make sure we have at least one layout -    if not info_data.get('layouts'): -        _find_missing_layouts(info_data, keyboard) - -    if not info_data.get('layouts'): -        _log_error(info_data, 'No LAYOUTs defined! Need at least one layout defined in the keyboard.h or info.json.') - -    # Filter out any non-existing community layouts -    for layout in info_data.get('community_layouts', []): -        if not _valid_community_layout(layout): -            # Ignore layout from future checks -            info_data['community_layouts'].remove(layout) -            _log_error(info_data, 'Claims to support a community layout that does not exist: %s' % (layout)) - -    # Make sure we supply layout macros for the community layouts we claim to support -    for layout in info_data.get('community_layouts', []): -        layout_name = 'LAYOUT_' + layout -        if layout_name not in info_data.get('layouts', {}) and layout_name not in info_data.get('layout_aliases', {}): -            _log_error(info_data, 'Claims to support community layout %s but no %s() macro found' % (layout, layout_name)) +    # Validate +    _validate(keyboard, info_data)      # Check that the reported matrix size is consistent with the actual matrix size      _check_matrix(info_data) @@ -701,30 +718,6 @@ def _search_keyboard_h(keyboard):      return layouts, aliases -def _find_missing_layouts(info_data, keyboard): -    """Looks for layout macros when they aren't found other places. - -    If we don't find any layouts from info.json or keyboard.h we widen our search. This is error prone which is why we want to encourage people to follow the standard above. -    """ -    _log_warning(info_data, '%s: Falling back to searching for KEYMAP/LAYOUT macros.' % (keyboard)) - -    for file in glob('keyboards/%s/*.h' % keyboard): -        these_layouts, these_aliases = find_layouts(file) - -        if these_layouts: -            for layout_name, layout_json in these_layouts.items(): -                if not layout_name.startswith('LAYOUT_kc'): -                    layout_json['c_macro'] = True -                    info_data['layouts'][layout_name] = layout_json - -        for alias, alias_text in these_aliases.items(): -            if alias_text in these_layouts: -                if 'layout_aliases' not in info_data: -                    info_data['layout_aliases'] = {} - -                info_data['layout_aliases'][alias] = alias_text - -  def _log_error(info_data, message):      """Send an error message to both JSON and the log.      """ | 
