From 17fec52b0fc337ee96dec020e7371ddd5e39cdea Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Wed, 30 Nov 2022 04:27:48 +0000 Subject: Extend layout lint checks (#19200) * Extend layout lint checks * Apply suggestions from code review Co-authored-by: Ryan * Fix function comment Co-authored-by: Ryan --- lib/python/qmk/info.py | 99 +++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 53 deletions(-) (limited to 'lib/python') 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 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. """ -- cgit v1.2.3