Extend layout lint checks (#19200)

* Extend layout lint checks

* Apply suggestions from code review

Co-authored-by: Ryan <fauxpark@gmail.com>

* Fix function comment

Co-authored-by: Ryan <fauxpark@gmail.com>
This commit is contained in:
Joel Challis 2022-11-30 04:27:48 +00:00 committed by GitHub
parent 4a0aa9ada2
commit 17fec52b0f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,6 +1,5 @@
"""Functions that help us generate and use info.json files. """Functions that help us generate and use info.json files.
""" """
from glob import glob
from pathlib import Path from pathlib import Path
import jsonschema import jsonschema
@ -26,6 +25,50 @@ def _valid_community_layout(layout):
return (Path('layouts/default') / layout).exists() 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): def info_json(keyboard):
"""Generate the info.json data for a specific keyboard. """Generate the info.json data for a specific keyboard.
""" """
@ -72,34 +115,8 @@ def info_json(keyboard):
# Merge in data from <keyboard.c> # Merge in data from <keyboard.c>
info_data = _extract_led_config(info_data, str(keyboard)) info_data = _extract_led_config(info_data, str(keyboard))
# Validate against the jsonschema # Validate
try: _validate(keyboard, info_data)
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))
# Check that the reported matrix size is consistent with the actual matrix size # Check that the reported matrix size is consistent with the actual matrix size
_check_matrix(info_data) _check_matrix(info_data)
@ -701,30 +718,6 @@ def _search_keyboard_h(keyboard):
return layouts, aliases 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): def _log_error(info_data, message):
"""Send an error message to both JSON and the log. """Send an error message to both JSON and the log.
""" """