aboutsummaryrefslogtreecommitdiff
path: root/lib/python/qmk/info.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python/qmk/info.py')
-rw-r--r--lib/python/qmk/info.py100
1 files changed, 74 insertions, 26 deletions
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py
index d23b3592e..858fbab33 100644
--- a/lib/python/qmk/info.py
+++ b/lib/python/qmk/info.py
@@ -9,7 +9,7 @@ from milc import cli
9 9
10from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS 10from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS
11from qmk.c_parse import find_layouts 11from qmk.c_parse import find_layouts
12from qmk.json_schema import deep_update, json_load, keyboard_validate, keyboard_api_validate 12from qmk.json_schema import deep_update, json_load, validate
13from qmk.keyboard import config_h, rules_mk 13from qmk.keyboard import config_h, rules_mk
14from qmk.keymap import list_keymaps 14from qmk.keymap import list_keymaps
15from qmk.makefile import parse_rules_mk_file 15from qmk.makefile import parse_rules_mk_file
@@ -49,7 +49,7 @@ def info_json(keyboard):
49 info_data['keymaps'][keymap.name] = {'url': f'https://raw.githubusercontent.com/qmk/qmk_firmware/master/{keymap}/keymap.json'} 49 info_data['keymaps'][keymap.name] = {'url': f'https://raw.githubusercontent.com/qmk/qmk_firmware/master/{keymap}/keymap.json'}
50 50
51 # Populate layout data 51 # Populate layout data
52 layouts, aliases = _find_all_layouts(info_data, keyboard) 52 layouts, aliases = _search_keyboard_h(keyboard)
53 53
54 if aliases: 54 if aliases:
55 info_data['layout_aliases'] = aliases 55 info_data['layout_aliases'] = aliases
@@ -64,9 +64,12 @@ def info_json(keyboard):
64 info_data = _extract_config_h(info_data) 64 info_data = _extract_config_h(info_data)
65 info_data = _extract_rules_mk(info_data) 65 info_data = _extract_rules_mk(info_data)
66 66
67 # Ensure that we have matrix row and column counts
68 info_data = _matrix_size(info_data)
69
67 # Validate against the jsonschema 70 # Validate against the jsonschema
68 try: 71 try:
69 keyboard_api_validate(info_data) 72 validate(info_data, 'qmk.api.keyboard.v1')
70 73
71 except jsonschema.ValidationError as e: 74 except jsonschema.ValidationError as e:
72 json_path = '.'.join([str(p) for p in e.absolute_path]) 75 json_path = '.'.join([str(p) for p in e.absolute_path])
@@ -75,6 +78,9 @@ def info_json(keyboard):
75 78
76 # Make sure we have at least one layout 79 # Make sure we have at least one layout
77 if not info_data.get('layouts'): 80 if not info_data.get('layouts'):
81 _find_missing_layouts(info_data, keyboard)
82
83 if not info_data.get('layouts'):
78 _log_error(info_data, 'No LAYOUTs defined! Need at least one layout defined in the keyboard.h or info.json.') 84 _log_error(info_data, 'No LAYOUTs defined! Need at least one layout defined in the keyboard.h or info.json.')
79 85
80 # Filter out any non-existing community layouts 86 # Filter out any non-existing community layouts
@@ -90,6 +96,9 @@ def info_json(keyboard):
90 if layout_name not in info_data.get('layouts', {}) and layout_name not in info_data.get('layout_aliases', {}): 96 if layout_name not in info_data.get('layouts', {}) and layout_name not in info_data.get('layout_aliases', {}):
91 _log_error(info_data, 'Claims to support community layout %s but no %s() macro found' % (layout, layout_name)) 97 _log_error(info_data, 'Claims to support community layout %s but no %s() macro found' % (layout, layout_name))
92 98
99 # Check that the reported matrix size is consistent with the actual matrix size
100 _check_matrix(info_data)
101
93 return info_data 102 return info_data
94 103
95 104
@@ -143,10 +152,7 @@ def _pin_name(pin):
143 elif pin == 'NO_PIN': 152 elif pin == 'NO_PIN':
144 return None 153 return None
145 154
146 elif pin[0] in 'ABCDEFGHIJK' and pin[1].isdigit(): 155 return pin
147 return pin
148
149 raise ValueError(f'Invalid pin: {pin}')
150 156
151 157
152def _extract_pins(pins): 158def _extract_pins(pins):
@@ -341,6 +347,46 @@ def _extract_rules_mk(info_data):
341 return info_data 347 return info_data
342 348
343 349
350def _matrix_size(info_data):
351 """Add info_data['matrix_size'] if it doesn't exist.
352 """
353 if 'matrix_size' not in info_data and 'matrix_pins' in info_data:
354 info_data['matrix_size'] = {}
355
356 if 'direct' in info_data['matrix_pins']:
357 info_data['matrix_size']['cols'] = len(info_data['matrix_pins']['direct'][0])
358 info_data['matrix_size']['rows'] = len(info_data['matrix_pins']['direct'])
359 elif 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']:
360 info_data['matrix_size']['cols'] = len(info_data['matrix_pins']['cols'])
361 info_data['matrix_size']['rows'] = len(info_data['matrix_pins']['rows'])
362
363 return info_data
364
365
366def _check_matrix(info_data):
367 """Check the matrix to ensure that row/column count is consistent.
368 """
369 if 'matrix_pins' in info_data and 'matrix_size' in info_data:
370 actual_col_count = info_data['matrix_size'].get('cols', 0)
371 actual_row_count = info_data['matrix_size'].get('rows', 0)
372 col_count = row_count = 0
373
374 if 'direct' in info_data['matrix_pins']:
375 col_count = len(info_data['matrix_pins']['direct'][0])
376 row_count = len(info_data['matrix_pins']['direct'])
377 elif 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']:
378 col_count = len(info_data['matrix_pins']['cols'])
379 row_count = len(info_data['matrix_pins']['rows'])
380
381 if col_count != actual_col_count and col_count != (actual_col_count / 2):
382 # FIXME: once we can we should detect if split is enabled to do the actual_col_count/2 check.
383 _log_error(info_data, f'MATRIX_COLS is inconsistent with the size of MATRIX_COL_PINS: {col_count} != {actual_col_count}')
384
385 if row_count != actual_row_count and row_count != (actual_row_count / 2):
386 # FIXME: once we can we should detect if split is enabled to do the actual_row_count/2 check.
387 _log_error(info_data, f'MATRIX_ROWS is inconsistent with the size of MATRIX_ROW_PINS: {row_count} != {actual_row_count}')
388
389
344def _merge_layouts(info_data, new_info_data): 390def _merge_layouts(info_data, new_info_data):
345 """Merge new_info_data into info_data in an intelligent way. 391 """Merge new_info_data into info_data in an intelligent way.
346 """ 392 """
@@ -374,12 +420,13 @@ def _merge_layouts(info_data, new_info_data):
374 return info_data 420 return info_data
375 421
376 422
377def _search_keyboard_h(path): 423def _search_keyboard_h(keyboard):
424 keyboard = Path(keyboard)
378 current_path = Path('keyboards/') 425 current_path = Path('keyboards/')
379 aliases = {} 426 aliases = {}
380 layouts = {} 427 layouts = {}
381 428
382 for directory in path.parts: 429 for directory in keyboard.parts:
383 current_path = current_path / directory 430 current_path = current_path / directory
384 keyboard_h = '%s.h' % (directory,) 431 keyboard_h = '%s.h' % (directory,)
385 keyboard_h_path = current_path / keyboard_h 432 keyboard_h_path = current_path / keyboard_h
@@ -394,27 +441,28 @@ def _search_keyboard_h(path):
394 return layouts, aliases 441 return layouts, aliases
395 442
396 443
397def _find_all_layouts(info_data, keyboard): 444def _find_missing_layouts(info_data, keyboard):
398 """Looks for layout macros associated with this keyboard. 445 """Looks for layout macros when they aren't found other places.
399 """
400 layouts, aliases = _search_keyboard_h(Path(keyboard))
401 446
402 if not layouts: 447 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.
403 # 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. 448 """
404 info_data['parse_warnings'].append('%s: Falling back to searching for KEYMAP/LAYOUT macros.' % (keyboard)) 449 _log_warning(info_data, '%s: Falling back to searching for KEYMAP/LAYOUT macros.' % (keyboard))
405 450
406 for file in glob('keyboards/%s/*.h' % keyboard): 451 for file in glob('keyboards/%s/*.h' % keyboard):
407 if file.endswith('.h'): 452 these_layouts, these_aliases = find_layouts(file)
408 these_layouts, these_aliases = find_layouts(file)
409 453
410 if these_layouts: 454 if these_layouts:
411 layouts.update(these_layouts) 455 for layout_name, layout_json in these_layouts.items():
456 if not layout_name.startswith('LAYOUT_kc'):
457 layout_json['c_macro'] = True
458 info_data['layouts'][layout_name] = layout_json
412 459
413 for alias, alias_text in these_aliases.items(): 460 for alias, alias_text in these_aliases.items():
414 if alias_text in layouts: 461 if alias_text in these_layouts:
415 aliases[alias] = alias_text 462 if 'layout_aliases' not in info_data:
463 info_data['layout_aliases'] = {}
416 464
417 return layouts, aliases 465 info_data['layout_aliases'][alias] = alias_text
418 466
419 467
420def _log_error(info_data, message): 468def _log_error(info_data, message):
@@ -493,7 +541,7 @@ def merge_info_jsons(keyboard, info_data):
493 continue 541 continue
494 542
495 try: 543 try:
496 keyboard_validate(new_info_data) 544 validate(new_info_data, 'qmk.keyboard.v1')
497 except jsonschema.ValidationError as e: 545 except jsonschema.ValidationError as e:
498 json_path = '.'.join([str(p) for p in e.absolute_path]) 546 json_path = '.'.join([str(p) for p in e.absolute_path])
499 cli.log.error('Not including data from file: %s', info_file) 547 cli.log.error('Not including data from file: %s', info_file)