mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-10-30 21:02:32 +01:00 
			
		
		
		
	get qmk generate-api into a good state
This commit is contained in:
		
							parent
							
								
									266a85eda0
								
							
						
					
					
						commit
						b2c26f7cdd
					
				| @ -90,6 +90,9 @@ | |||||||
|                 "type": "object", |                 "type": "object", | ||||||
|                 "additionalProperties": false, |                 "additionalProperties": false, | ||||||
|                 "properties": { |                 "properties": { | ||||||
|  |                     "filename": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|                     "c_macro": { |                     "c_macro": { | ||||||
|                         "type": "boolean" |                         "type": "boolean" | ||||||
|                     }, |                     }, | ||||||
| @ -119,6 +122,18 @@ | |||||||
|                                     "type": "number", |                                     "type": "number", | ||||||
|                                     "min": 0.25 |                                     "min": 0.25 | ||||||
|                                 }, |                                 }, | ||||||
|  |                                 "r": { | ||||||
|  |                                     "type": "number", | ||||||
|  |                                     "min": 0 | ||||||
|  |                                 }, | ||||||
|  |                                 "rx": { | ||||||
|  |                                     "type": "number", | ||||||
|  |                                     "min": 0 | ||||||
|  |                                 }, | ||||||
|  |                                 "ry": { | ||||||
|  |                                     "type": "number", | ||||||
|  |                                     "min": 0 | ||||||
|  |                                 }, | ||||||
|                                 "w": { |                                 "w": { | ||||||
|                                     "type": "number", |                                     "type": "number", | ||||||
|                                     "min": 0.25 |                                     "min": 0.25 | ||||||
| @ -199,6 +214,12 @@ | |||||||
|                     "min": 0, |                     "min": 0, | ||||||
|                     "multipleOf": 1 |                     "multipleOf": 1 | ||||||
|                 }, |                 }, | ||||||
|  |                 "max_brightness": { | ||||||
|  |                     "type": "number", | ||||||
|  |                     "min": 0, | ||||||
|  |                     "max": 255, | ||||||
|  |                     "multipleOf": 1 | ||||||
|  |                 }, | ||||||
|                 "pin": { |                 "pin": { | ||||||
|                     "type": "string", |                     "type": "string", | ||||||
|                     "pattern": "^[A-K]\\d{1,2}$" |                     "pattern": "^[A-K]\\d{1,2}$" | ||||||
| @ -207,6 +228,18 @@ | |||||||
|                     "type": "number", |                     "type": "number", | ||||||
|                     "min": 0, |                     "min": 0, | ||||||
|                     "multipleOf": 1 |                     "multipleOf": 1 | ||||||
|  |                 }, | ||||||
|  |                 "sleep": {"type": "boolean"}, | ||||||
|  |                 "split": {"type": "boolean"}, | ||||||
|  |                 "split_count": { | ||||||
|  |                     "type": "array", | ||||||
|  |                     "minLength": 2, | ||||||
|  |                     "maxLength": 2, | ||||||
|  |                     "items": { | ||||||
|  |                         "type": "number", | ||||||
|  |                         "min": 0, | ||||||
|  |                         "multipleOf": 1 | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  | |||||||
| @ -1,12 +1,27 @@ | |||||||
| """Functions for working with config.h files. | """Functions for working with config.h files. | ||||||
| """ | """ | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  | import re | ||||||
| 
 | 
 | ||||||
| from milc import cli | from milc import cli | ||||||
| 
 | 
 | ||||||
| from qmk.comment_remover import comment_remover | from qmk.comment_remover import comment_remover | ||||||
| 
 | 
 | ||||||
| default_key_entry = {'x': -1, 'y': 0, 'w': 1} | default_key_entry = {'x': -1, 'y': 0, 'w': 1} | ||||||
|  | single_comment_regex = re.compile(r' */[/*].*$') | ||||||
|  | multi_comment_regex = re.compile(r'/\*(.|\n)*\*/', re.MULTILINE) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def strip_line_comment(string): | ||||||
|  |     """Removes comments from a single line string. | ||||||
|  |     """ | ||||||
|  |     return single_comment_regex.sub('', string) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def strip_multiline_comment(string): | ||||||
|  |     """Removes comments from a single line string. | ||||||
|  |     """ | ||||||
|  |     return multi_comment_regex.sub('', string) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def c_source_files(dir_names): | def c_source_files(dir_names): | ||||||
| @ -53,7 +68,8 @@ def find_layouts(file): | |||||||
|             parsed_layout = [_default_key(key) for key in layout.split(',')] |             parsed_layout = [_default_key(key) for key in layout.split(',')] | ||||||
| 
 | 
 | ||||||
|             for key in parsed_layout: |             for key in parsed_layout: | ||||||
|                 key['matrix'] = matrix_locations.get(key['label']) |                 if key['label'] in matrix_locations: | ||||||
|  |                     key['matrix'] = matrix_locations[key['label']] | ||||||
| 
 | 
 | ||||||
|             parsed_layouts[macro_name] = { |             parsed_layouts[macro_name] = { | ||||||
|                 'key_count': len(parsed_layout), |                 'key_count': len(parsed_layout), | ||||||
| @ -88,12 +104,10 @@ def parse_config_h_file(config_h_file, config_h=None): | |||||||
|     if config_h_file.exists(): |     if config_h_file.exists(): | ||||||
|         config_h_text = config_h_file.read_text() |         config_h_text = config_h_file.read_text() | ||||||
|         config_h_text = config_h_text.replace('\\\n', '') |         config_h_text = config_h_text.replace('\\\n', '') | ||||||
|  |         config_h_text = strip_multiline_comment(config_h_text) | ||||||
| 
 | 
 | ||||||
|         for linenum, line in enumerate(config_h_text.split('\n')): |         for linenum, line in enumerate(config_h_text.split('\n')): | ||||||
|             line = line.strip() |             line = strip_line_comment(line).strip() | ||||||
| 
 |  | ||||||
|             if '//' in line: |  | ||||||
|                 line = line[:line.index('//')].strip() |  | ||||||
| 
 | 
 | ||||||
|             if not line: |             if not line: | ||||||
|                 continue |                 continue | ||||||
| @ -156,6 +170,6 @@ def _parse_matrix_locations(matrix, file, macro_name): | |||||||
|         row = row.replace('{', '').replace('}', '') |         row = row.replace('{', '').replace('}', '') | ||||||
|         for col_num, identifier in enumerate(row.split(',')): |         for col_num, identifier in enumerate(row.split(',')): | ||||||
|             if identifier != 'KC_NO': |             if identifier != 'KC_NO': | ||||||
|                 matrix_locations[identifier] = (row_num, col_num) |                 matrix_locations[identifier] = [row_num, col_num] | ||||||
| 
 | 
 | ||||||
|     return matrix_locations |     return matrix_locations | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ def generate_api(cli): | |||||||
|             if 'vid' in usb and usb['vid'] not in usb_list['devices']: |             if 'vid' in usb and usb['vid'] not in usb_list['devices']: | ||||||
|                 usb_list['devices'][usb['vid']] = {} |                 usb_list['devices'][usb['vid']] = {} | ||||||
| 
 | 
 | ||||||
|             if 'pid' in usb and usb['pid'] not in usb_list['devices'][usb['vid']]: |             if 'vid' in usb and usb['pid'] not in usb_list['devices'][usb['vid']]: | ||||||
|                 usb_list['devices'][usb['vid']][usb['pid']] = {} |                 usb_list['devices'][usb['vid']][usb['pid']] = {} | ||||||
| 
 | 
 | ||||||
|             if 'vid' in usb and 'pid' in usb: |             if 'vid' in usb and 'pid' in usb: | ||||||
|  | |||||||
| @ -41,9 +41,12 @@ def generate_rules_mk(cli): | |||||||
|     # Find features that should be enabled |     # Find features that should be enabled | ||||||
|     if 'features' in kb_info_json: |     if 'features' in kb_info_json: | ||||||
|         for feature, enabled in kb_info_json['features'].items(): |         for feature, enabled in kb_info_json['features'].items(): | ||||||
|             feature = feature.upper() |             if feature == 'bootmagic_lite' and enabled: | ||||||
|             enabled = 'yes' if enabled else 'no' |                 rules_mk_lines.append(f'BOOTMAGIC_ENABLE := lite') | ||||||
|             rules_mk_lines.append(f'{feature}_ENABLE := {enabled}') |             else: | ||||||
|  |                 feature = feature.upper() | ||||||
|  |                 enabled = 'yes' if enabled else 'no' | ||||||
|  |                 rules_mk_lines.append(f'{feature}_ENABLE := {enabled}') | ||||||
| 
 | 
 | ||||||
|     # Set the LED driver |     # Set the LED driver | ||||||
|     if 'led_matrix' in kb_info_json and 'driver' in kb_info_json['led_matrix']: |     if 'led_matrix' in kb_info_json and 'driver' in kb_info_json['led_matrix']: | ||||||
|  | |||||||
| @ -26,13 +26,12 @@ led_matrix_properties = { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| rgblight_properties = { | rgblight_properties = { | ||||||
|     'led_count': 'RGBLED_NUM', |     'led_count': ('RGBLED_NUM', int), | ||||||
|     'pin': 'RGB_DI_PIN', |     'pin': ('RGB_DI_PIN', str), | ||||||
|     'split_count': 'RGBLED_SPLIT', |     'max_brightness': ('RGBLIGHT_LIMIT_VAL', int), | ||||||
|     'max_brightness': 'RGBLIGHT_LIMIT_VAL', |     'hue_steps': ('RGBLIGHT_HUE_STEP', int), | ||||||
|     'hue_steps': 'RGBLIGHT_HUE_STEP', |     'saturation_steps': ('RGBLIGHT_SAT_STEP', int), | ||||||
|     'saturation_steps': 'RGBLIGHT_SAT_STEP', |     'brightness_steps': ('RGBLIGHT_VAL_STEP', int) | ||||||
|     'brightness_steps': 'RGBLIGHT_VAL_STEP' |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| rgblight_toggles = { | rgblight_toggles = { | ||||||
| @ -54,6 +53,8 @@ rgblight_animations = { | |||||||
|     'twinkle': 'RGBLIGHT_EFFECT_TWINKLE' |     'twinkle': 'RGBLIGHT_EFFECT_TWINKLE' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | usb_properties = {'vid': 'VENDOR_ID', 'pid': 'PRODUCT_ID', 'device_ver': 'DEVICE_VER'} | ||||||
|  | 
 | ||||||
| true_values = ['1', 'on', 'yes'] | true_values = ['1', 'on', 'yes'] | ||||||
| false_values = ['0', 'off', 'no'] | false_values = ['0', 'off', 'no'] | ||||||
| 
 | 
 | ||||||
| @ -97,7 +98,8 @@ def info_json(keyboard): | |||||||
|         keyboard_api_validate(info_data) |         keyboard_api_validate(info_data) | ||||||
| 
 | 
 | ||||||
|     except jsonschema.ValidationError as e: |     except jsonschema.ValidationError as e: | ||||||
|         cli.log.error('Invalid info.json data: %s', e.message) |         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) | ||||||
|         print(dir(e)) |         print(dir(e)) | ||||||
|         exit() |         exit() | ||||||
| 
 | 
 | ||||||
| @ -198,6 +200,9 @@ def _extract_indicators(info_data, config_c): | |||||||
|         if json_key in info_data.get('indicators', []) and config_key in config_c: |         if json_key in info_data.get('indicators', []) and config_key in config_c: | ||||||
|             _log_warning(info_data, f'Indicator {json_key} is specified in both info.json and config.h, the config.h value wins.') |             _log_warning(info_data, f'Indicator {json_key} is specified in both info.json and config.h, the config.h value wins.') | ||||||
| 
 | 
 | ||||||
|  |         if 'indicators' not in info_data: | ||||||
|  |             info_data['indicators'] = {} | ||||||
|  | 
 | ||||||
|         if config_key in config_c: |         if config_key in config_c: | ||||||
|             if 'indicators' not in info_data: |             if 'indicators' not in info_data: | ||||||
|                 info_data['indicators'] = {} |                 info_data['indicators'] = {} | ||||||
| @ -226,10 +231,23 @@ def _extract_community_layouts(info_data, rules): | |||||||
| def _extract_features(info_data, rules): | def _extract_features(info_data, rules): | ||||||
|     """Find all the features enabled in rules.mk. |     """Find all the features enabled in rules.mk. | ||||||
|     """ |     """ | ||||||
|  |     # Special handling for bootmagic which also supports a "lite" mode. | ||||||
|  |     if rules.get('BOOTMAGIC_ENABLE') == 'lite': | ||||||
|  |         rules['BOOTMAGIC_LITE_ENABLE'] = 'on' | ||||||
|  |         del(rules['BOOTMAGIC_ENABLE']) | ||||||
|  |     if rules.get('BOOTMAGIC_ENABLE') == 'full': | ||||||
|  |         rules['BOOTMAGIC_ENABLE'] = 'on' | ||||||
|  | 
 | ||||||
|  |     # Skip non-boolean features we haven't implemented special handling for | ||||||
|  |     for feature in 'HAPTIC_ENABLE', 'QWIIC_ENABLE': | ||||||
|  |         if rules.get(feature): | ||||||
|  |             del(rules[feature]) | ||||||
|  | 
 | ||||||
|  |     # Process the rest of the rules as booleans | ||||||
|     for key, value in rules.items(): |     for key, value in rules.items(): | ||||||
|         if key.endswith('_ENABLE'): |         if key.endswith('_ENABLE'): | ||||||
|             key = '_'.join(key.split('_')[:-1]).lower() |             key = '_'.join(key.split('_')[:-1]).lower() | ||||||
|             value = True if value in true_values else False if value in false_values else value |             value = True if value.lower() in true_values else False if value.lower() in false_values else value | ||||||
| 
 | 
 | ||||||
|             if 'config_h_features' not in info_data: |             if 'config_h_features' not in info_data: | ||||||
|                 info_data['config_h_features'] = {} |                 info_data['config_h_features'] = {} | ||||||
| @ -280,12 +298,21 @@ def _extract_rgblight(info_data, config_c): | |||||||
|     rgblight = info_data.get('rgblight', {}) |     rgblight = info_data.get('rgblight', {}) | ||||||
|     animations = rgblight.get('animations', {}) |     animations = rgblight.get('animations', {}) | ||||||
| 
 | 
 | ||||||
|     for json_key, config_key in rgblight_properties.items(): |     if 'RGBLED_SPLIT' in config_c: | ||||||
|  |         raw_split = config_c.get('RGBLED_SPLIT', '').replace('{', '').replace('}', '').strip() | ||||||
|  |         rgblight['split_count'] = [int(i) for i in raw_split.split(',')] | ||||||
|  | 
 | ||||||
|  |     for json_key, config_key_type in rgblight_properties.items(): | ||||||
|  |         config_key, config_type = config_key_type | ||||||
|  | 
 | ||||||
|         if config_key in config_c: |         if config_key in config_c: | ||||||
|             if json_key in rgblight: |             if json_key in rgblight: | ||||||
|                 _log_warning(info_data, 'RGB Light: %s is specified in both info.json and config.h, the config.h value wins.' % (json_key,)) |                 _log_warning(info_data, 'RGB Light: %s is specified in both info.json and config.h, the config.h value wins.' % (json_key,)) | ||||||
| 
 | 
 | ||||||
|             rgblight[json_key] = config_c[config_key] |             try: | ||||||
|  |                 rgblight[json_key] = config_type(config_c[config_key]) | ||||||
|  |             except ValueError as e: | ||||||
|  |                 cli.log.error('%s: config.h: Could not convert "%s" to %s: %s', info_data['keyboard_folder'], config_c[config_key], config_type.__name__, e) | ||||||
| 
 | 
 | ||||||
|     for json_key, config_key in rgblight_toggles.items(): |     for json_key, config_key in rgblight_toggles.items(): | ||||||
|         if config_key in config_c: |         if config_key in config_c: | ||||||
| @ -332,11 +359,16 @@ def _extract_matrix_info(info_data, config_c): | |||||||
| 
 | 
 | ||||||
|         info_data['matrix_pins'] = {} |         info_data['matrix_pins'] = {} | ||||||
| 
 | 
 | ||||||
|  |         # FIXME(skullydazed/anyone): Should really check every pin, not just the first | ||||||
|         if row_pins: |         if row_pins: | ||||||
|             info_data['matrix_pins']['rows'] = row_pins.split(',') |             row_pins = [pin.strip() for pin in row_pins.split(',') if pin] | ||||||
|  |             if row_pins[0][0] in 'ABCDEFGHIJK' and row_pins[0][1].isdigit(): | ||||||
|  |                 info_data['matrix_pins']['rows'] = row_pins | ||||||
| 
 | 
 | ||||||
|         if col_pins: |         if col_pins: | ||||||
|             info_data['matrix_pins']['cols'] = col_pins.split(',') |             col_pins = [pin.strip() for pin in col_pins.split(',') if pin] | ||||||
|  |             if col_pins[0][0] in 'ABCDEFGHIJK' and col_pins[0][1].isdigit(): | ||||||
|  |                 info_data['matrix_pins']['cols'] = col_pins | ||||||
| 
 | 
 | ||||||
|     if direct_pins: |     if direct_pins: | ||||||
|         if 'matrix_pins' in info_data: |         if 'matrix_pins' in info_data: | ||||||
| @ -345,6 +377,9 @@ def _extract_matrix_info(info_data, config_c): | |||||||
|         info_data['matrix_pins'] = {} |         info_data['matrix_pins'] = {} | ||||||
|         direct_pin_array = [] |         direct_pin_array = [] | ||||||
| 
 | 
 | ||||||
|  |         while direct_pins[-1] != '}': | ||||||
|  |             direct_pins = direct_pins[:-1] | ||||||
|  | 
 | ||||||
|         for row in direct_pins.split('},{'): |         for row in direct_pins.split('},{'): | ||||||
|             if row.startswith('{'): |             if row.startswith('{'): | ||||||
|                 row = row[1:] |                 row = row[1:] | ||||||
| @ -368,8 +403,6 @@ def _extract_matrix_info(info_data, config_c): | |||||||
| def _extract_usb_info(info_data, config_c): | def _extract_usb_info(info_data, config_c): | ||||||
|     """Populate the USB information. |     """Populate the USB information. | ||||||
|     """ |     """ | ||||||
|     usb_properties = {'vid': 'VENDOR_ID', 'pid': 'PRODUCT_ID', 'device_ver': 'DEVICE_VER'} |  | ||||||
| 
 |  | ||||||
|     if 'usb' not in info_data: |     if 'usb' not in info_data: | ||||||
|         info_data['usb'] = {} |         info_data['usb'] = {} | ||||||
| 
 | 
 | ||||||
| @ -378,10 +411,7 @@ def _extract_usb_info(info_data, config_c): | |||||||
|             if info_name in info_data['usb']: |             if info_name in info_data['usb']: | ||||||
|                 _log_warning(info_data, '%s in config.h is overwriting usb.%s in info.json' % (config_name, info_name)) |                 _log_warning(info_data, '%s in config.h is overwriting usb.%s in info.json' % (config_name, info_name)) | ||||||
| 
 | 
 | ||||||
|             info_data['usb'][info_name] = config_c[config_name] |             info_data['usb'][info_name] = '0x' + config_c[config_name][2:].upper() | ||||||
| 
 |  | ||||||
|         elif info_name not in info_data['usb']: |  | ||||||
|             _log_error(info_data, '%s not specified in config.h, and %s not specified in info.json. One is required.' % (config_name, info_name)) |  | ||||||
| 
 | 
 | ||||||
|     return info_data |     return info_data | ||||||
| 
 | 
 | ||||||
| @ -519,8 +549,9 @@ def arm_processor_rules(info_data, rules): | |||||||
|         info_data['processor'] = 'unknown' |         info_data['processor'] = 'unknown' | ||||||
| 
 | 
 | ||||||
|     if 'BOOTLOADER' in rules: |     if 'BOOTLOADER' in rules: | ||||||
|         if 'bootloader' in info_data: |         # FIXME(skullydazed/anyone): need to remove the massive amounts of duplication first | ||||||
|             _log_warning(info_data, 'Bootloader is specified in both info.json and rules.mk, the rules.mk value wins.') |         # if 'bootloader' in info_data: | ||||||
|  |         #     _log_warning(info_data, 'Bootloader is specified in both info.json and rules.mk, the rules.mk value wins.') | ||||||
| 
 | 
 | ||||||
|         info_data['bootloader'] = rules['BOOTLOADER'] |         info_data['bootloader'] = rules['BOOTLOADER'] | ||||||
| 
 | 
 | ||||||
| @ -558,8 +589,9 @@ def avr_processor_rules(info_data, rules): | |||||||
|         info_data['processor'] = 'unknown' |         info_data['processor'] = 'unknown' | ||||||
| 
 | 
 | ||||||
|     if 'BOOTLOADER' in rules: |     if 'BOOTLOADER' in rules: | ||||||
|         if 'bootloader' in info_data: |         # FIXME(skullydazed/anyone): need to remove the massive amounts of duplication first | ||||||
|             _log_warning(info_data, 'Bootloader is specified in both info.json and rules.mk, the rules.mk value wins.') |         # if 'bootloader' in info_data: | ||||||
|  |         #     _log_warning(info_data, 'Bootloader is specified in both info.json and rules.mk, the rules.mk value wins.') | ||||||
| 
 | 
 | ||||||
|         info_data['bootloader'] = rules['BOOTLOADER'] |         info_data['bootloader'] = rules['BOOTLOADER'] | ||||||
|     else: |     else: | ||||||
| @ -593,8 +625,8 @@ def merge_info_jsons(keyboard, info_data): | |||||||
|             keyboard_validate(new_info_data) |             keyboard_validate(new_info_data) | ||||||
| 
 | 
 | ||||||
|         except jsonschema.ValidationError as e: |         except jsonschema.ValidationError as e: | ||||||
|             cli.log.error('Invalid info.json data: %s', e.message) |             json_path = '.'.join([str(p) for p in e.absolute_path]) | ||||||
|             cli.log.error('Not including file %s', info_file) |             cli.log.error('Invalid info.json data: %s: %s: %s', info_file, json_path, e.message) | ||||||
|             continue |             continue | ||||||
| 
 | 
 | ||||||
|         if not isinstance(new_info_data, dict): |         if not isinstance(new_info_data, dict): | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Zach White
						Zach White