Merge remote-tracking branch 'origin/master' into develop
This commit is contained in:
		@@ -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.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user