summaryrefslogtreecommitdiff
path: root/lib/python/qmk/cli/generate
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python/qmk/cli/generate')
-rwxr-xr-xlib/python/qmk/cli/generate/api.py56
-rwxr-xr-xlib/python/qmk/cli/generate/keyboard_c.py22
-rwxr-xr-xlib/python/qmk/cli/generate/keyboard_h.py23
-rw-r--r--lib/python/qmk/cli/generate/keycodes.py65
-rw-r--r--lib/python/qmk/cli/generate/keycodes_tests.py39
-rw-r--r--lib/python/qmk/cli/generate/version_h.py13
6 files changed, 177 insertions, 41 deletions
diff --git a/lib/python/qmk/cli/generate/api.py b/lib/python/qmk/cli/generate/api.py
index 8650a36b84..11d4616199 100755
--- a/lib/python/qmk/cli/generate/api.py
+++ b/lib/python/qmk/cli/generate/api.py
@@ -10,8 +10,9 @@ from qmk.datetime import current_datetime
from qmk.info import info_json
from qmk.json_encoders import InfoJSONEncoder
from qmk.json_schema import json_load
+from qmk.keymap import list_keymaps
from qmk.keyboard import find_readme, list_keyboards
-from qmk.keycodes import load_spec, list_versions
+from qmk.keycodes import load_spec, list_versions, list_languages
DATA_PATH = Path('data')
TEMPLATE_PATH = DATA_PATH / 'templates/api/'
@@ -42,7 +43,14 @@ def _resolve_keycode_specs(output_folder):
overall = load_spec(version)
output_file = output_folder / f'constants/keycodes_{version}.json'
- output_file.write_text(json.dumps(overall, indent=4), encoding='utf-8')
+ output_file.write_text(json.dumps(overall), encoding='utf-8')
+
+ for lang in list_languages():
+ for version in list_versions(lang):
+ overall = load_spec(version, lang)
+
+ output_file = output_folder / f'constants/keycodes_{lang}_{version}.json'
+ output_file.write_text(json.dumps(overall, indent=4), encoding='utf-8')
# Purge files consumed by 'load_spec'
shutil.rmtree(output_folder / 'constants/keycodes/')
@@ -56,7 +64,7 @@ def _filtered_copy(src, dst):
data = json_load(src)
dst = dst.with_suffix('.json')
- dst.write_text(json.dumps(data, indent=4), encoding='utf-8')
+ dst.write_text(json.dumps(data), encoding='utf-8')
return dst
return shutil.copy2(src, dst)
@@ -103,24 +111,44 @@ def generate_api(cli):
# Generate and write keyboard specific JSON files
for keyboard_name in keyboard_list:
- kb_all[keyboard_name] = info_json(keyboard_name)
+ kb_json = info_json(keyboard_name)
+ kb_all[keyboard_name] = kb_json
+
keyboard_dir = v1_dir / 'keyboards' / keyboard_name
keyboard_info = keyboard_dir / 'info.json'
keyboard_readme = keyboard_dir / 'readme.md'
keyboard_readme_src = find_readme(keyboard_name)
+ # Populate the list of JSON keymaps
+ for keymap in list_keymaps(keyboard_name, c=False, fullpath=True):
+ kb_json['keymaps'][keymap.name] = {
+ # TODO: deprecate 'url' as consumer needs to know its potentially hjson
+ 'url': f'https://raw.githubusercontent.com/qmk/qmk_firmware/master/{keymap}/keymap.json',
+
+ # Instead consumer should grab from API and not repo directly
+ 'path': (keymap / 'keymap.json').as_posix(),
+ }
+
keyboard_dir.mkdir(parents=True, exist_ok=True)
- keyboard_json = json.dumps({'last_updated': current_datetime(), 'keyboards': {keyboard_name: kb_all[keyboard_name]}})
+ keyboard_json = json.dumps({'last_updated': current_datetime(), 'keyboards': {keyboard_name: kb_json}})
if not cli.args.dry_run:
- keyboard_info.write_text(keyboard_json)
+ keyboard_info.write_text(keyboard_json, encoding='utf-8')
cli.log.debug('Wrote file %s', keyboard_info)
if keyboard_readme_src:
shutil.copyfile(keyboard_readme_src, keyboard_readme)
cli.log.debug('Copied %s -> %s', keyboard_readme_src, keyboard_readme)
- if 'usb' in kb_all[keyboard_name]:
- usb = kb_all[keyboard_name]['usb']
+ # resolve keymaps as json
+ for keymap in kb_json['keymaps']:
+ keymap_hjson = kb_json['keymaps'][keymap]['path']
+ keymap_json = v1_dir / keymap_hjson
+ keymap_json.parent.mkdir(parents=True, exist_ok=True)
+ keymap_json.write_text(json.dumps(json_load(Path(keymap_hjson))), encoding='utf-8')
+ cli.log.debug('Wrote keymap %s', keymap_json)
+
+ if 'usb' in kb_json:
+ usb = kb_json['usb']
if 'vid' in usb and usb['vid'] not in usb_list:
usb_list[usb['vid']] = {}
@@ -153,9 +181,9 @@ def generate_api(cli):
constants_metadata_json = json.dumps({'last_updated': current_datetime(), 'constants': _list_constants(v1_dir)})
if not cli.args.dry_run:
- keyboard_all_file.write_text(keyboard_all_json)
- usb_file.write_text(usb_json)
- keyboard_list_file.write_text(keyboard_list_json)
- keyboard_aliases_file.write_text(keyboard_aliases_json)
- keyboard_metadata_file.write_text(keyboard_metadata_json)
- constants_metadata_file.write_text(constants_metadata_json)
+ keyboard_all_file.write_text(keyboard_all_json, encoding='utf-8')
+ usb_file.write_text(usb_json, encoding='utf-8')
+ keyboard_list_file.write_text(keyboard_list_json, encoding='utf-8')
+ keyboard_aliases_file.write_text(keyboard_aliases_json, encoding='utf-8')
+ keyboard_metadata_file.write_text(keyboard_metadata_json, encoding='utf-8')
+ constants_metadata_file.write_text(constants_metadata_json, encoding='utf-8')
diff --git a/lib/python/qmk/cli/generate/keyboard_c.py b/lib/python/qmk/cli/generate/keyboard_c.py
index a9b742f323..9004b41abb 100755
--- a/lib/python/qmk/cli/generate/keyboard_c.py
+++ b/lib/python/qmk/cli/generate/keyboard_c.py
@@ -25,17 +25,17 @@ def _gen_led_config(info_data):
if not config_type:
return lines
- matrix = [['NO_LED'] * cols for i in range(rows)]
+ matrix = [['NO_LED'] * cols for _ in range(rows)]
pos = []
flags = []
- led_config = info_data[config_type]['layout']
- for index, item in enumerate(led_config, start=0):
- if 'matrix' in item:
- (x, y) = item['matrix']
- matrix[x][y] = str(index)
- pos.append(f'{{ {item.get("x", 0)},{item.get("y", 0)} }}')
- flags.append(str(item.get('flags', 0)))
+ led_layout = info_data[config_type]['layout']
+ for index, led_data in enumerate(led_layout):
+ if 'matrix' in led_data:
+ row, col = led_data['matrix']
+ matrix[row][col] = str(index)
+ pos.append(f'{{{led_data.get("x", 0)}, {led_data.get("y", 0)}}}')
+ flags.append(str(led_data.get('flags', 0)))
if config_type == 'rgb_matrix':
lines.append('#ifdef RGB_MATRIX_ENABLE')
@@ -47,10 +47,10 @@ def _gen_led_config(info_data):
lines.append('__attribute__ ((weak)) led_config_t g_led_config = {')
lines.append(' {')
for line in matrix:
- lines.append(f' {{ {",".join(line)} }},')
+ lines.append(f' {{ {", ".join(line)} }},')
lines.append(' },')
- lines.append(f' {{ {",".join(pos)} }},')
- lines.append(f' {{ {",".join(flags)} }},')
+ lines.append(f' {{ {", ".join(pos)} }},')
+ lines.append(f' {{ {", ".join(flags)} }},')
lines.append('};')
lines.append('#endif')
diff --git a/lib/python/qmk/cli/generate/keyboard_h.py b/lib/python/qmk/cli/generate/keyboard_h.py
index 910bd6a08d..152921bdce 100755
--- a/lib/python/qmk/cli/generate/keyboard_h.py
+++ b/lib/python/qmk/cli/generate/keyboard_h.py
@@ -25,32 +25,31 @@ def _generate_layouts(keyboard):
row_num = kb_info_json['matrix_size']['rows']
lines = []
- for layout_name in kb_info_json['layouts']:
- if kb_info_json['layouts'][layout_name]['c_macro']:
+ for layout_name, layout_data in kb_info_json['layouts'].items():
+ if layout_data['c_macro']:
continue
- if 'matrix' not in kb_info_json['layouts'][layout_name]['layout'][0]:
- cli.log.debug(f'{keyboard}/{layout_name}: No matrix data!')
+ if not all('matrix' in key_data for key_data in layout_data['layout']):
+ cli.log.debug(f'{keyboard}/{layout_name}: No or incomplete matrix data!')
continue
layout_keys = []
- layout_matrix = [['KC_NO' for i in range(col_num)] for i in range(row_num)]
+ layout_matrix = [['KC_NO'] * col_num for _ in range(row_num)]
- for i, key in enumerate(kb_info_json['layouts'][layout_name]['layout']):
- row = key['matrix'][0]
- col = key['matrix'][1]
- identifier = 'k%s%s' % (ROW_LETTERS[row], COL_LETTERS[col])
+ for index, key_data in enumerate(layout_data['layout']):
+ row, col = key_data['matrix']
+ identifier = f'k{ROW_LETTERS[row]}{COL_LETTERS[col]}'
try:
layout_matrix[row][col] = identifier
layout_keys.append(identifier)
except IndexError:
- key_name = key.get('label', identifier)
- cli.log.error(f'Matrix data out of bounds for layout {layout_name} at index {i} ({key_name}): [{row}, {col}]')
+ key_name = key_data.get('label', identifier)
+ cli.log.error(f'{keyboard}/{layout_name}: Matrix data out of bounds at index {index} ({key_name}): [{row}, {col}]')
return []
lines.append('')
- lines.append('#define %s(%s) {\\' % (layout_name, ', '.join(layout_keys)))
+ lines.append(f'#define {layout_name}({", ".join(layout_keys)}) {{ \\')
rows = ', \\\n'.join(['\t {' + ', '.join(row) + '}' for row in layout_matrix])
rows += ' \\'
diff --git a/lib/python/qmk/cli/generate/keycodes.py b/lib/python/qmk/cli/generate/keycodes.py
index 29b7db3c80..f5c646e0ea 100644
--- a/lib/python/qmk/cli/generate/keycodes.py
+++ b/lib/python/qmk/cli/generate/keycodes.py
@@ -8,6 +8,32 @@ from qmk.path import normpath
from qmk.keycodes import load_spec
+def _translate_group(group):
+ """Fix up any issues with badly chosen values
+ """
+ if group == 'modifiers':
+ return 'modifier'
+ return group
+
+
+def _render_key(key):
+ width = 7
+ if 'S(' in key:
+ width += len('S()')
+ if 'A(' in key:
+ width += len('A()')
+ if 'RCTL(' in key:
+ width += len('RCTL()')
+ if 'ALGR(' in key:
+ width += len('ALGR()')
+ return key.ljust(width)
+
+
+def _render_label(label):
+ label = label.replace("\\", "(backslash)")
+ return label
+
+
def _generate_ranges(lines, keycodes):
lines.append('')
lines.append('enum qk_keycode_ranges {')
@@ -64,7 +90,24 @@ def _generate_helpers(lines, keycodes):
for group, codes in temp.items():
lo = keycodes["keycodes"][f'0x{codes[0]:04X}']['key']
hi = keycodes["keycodes"][f'0x{codes[1]:04X}']['key']
- lines.append(f'#define IS_{ group.upper() }_KEYCODE(code) ((code) >= {lo} && (code) <= {hi})')
+ lines.append(f'#define IS_{ _translate_group(group).upper() }_KEYCODE(code) ((code) >= {lo} && (code) <= {hi})')
+
+
+def _generate_aliases(lines, keycodes):
+ lines.append('')
+ lines.append('// Aliases')
+ for key, value in keycodes["aliases"].items():
+ define = _render_key(value.get("key"))
+ val = _render_key(key)
+ if 'label' in value:
+ lines.append(f'#define {define} {val} // {_render_label(value.get("label"))}')
+ else:
+ lines.append(f'#define {define} {val}')
+
+ lines.append('')
+ for key, value in keycodes["aliases"].items():
+ for alias in value.get("aliases", []):
+ lines.append(f'#define {alias} {value.get("key")}')
@cli.argument('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.')
@@ -86,3 +129,23 @@ def generate_keycodes(cli):
# Show the results
dump_lines(cli.args.output, keycodes_h_lines, cli.args.quiet)
+
+
+@cli.argument('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.')
+@cli.argument('-l', '--lang', arg_only=True, required=True, help='Language of keycodes to generate.')
+@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
+@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
+@cli.subcommand('Used by the make system to generate keymap_{lang}.h from keycodes_{lang}_{version}.json', hidden=True)
+def generate_keycode_extras(cli):
+ """Generates the header file.
+ """
+
+ # Build the header file.
+ keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '#include "keymap.h"', '// clang-format off']
+
+ keycodes = load_spec(cli.args.version, cli.args.lang)
+
+ _generate_aliases(keycodes_h_lines, keycodes)
+
+ # Show the results
+ dump_lines(cli.args.output, keycodes_h_lines, cli.args.quiet)
diff --git a/lib/python/qmk/cli/generate/keycodes_tests.py b/lib/python/qmk/cli/generate/keycodes_tests.py
new file mode 100644
index 0000000000..453b4693a7
--- /dev/null
+++ b/lib/python/qmk/cli/generate/keycodes_tests.py
@@ -0,0 +1,39 @@
+"""Used by the make system to generate a keycode lookup table from keycodes_{version}.json
+"""
+from milc import cli
+
+from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
+from qmk.commands import dump_lines
+from qmk.path import normpath
+from qmk.keycodes import load_spec
+
+
+def _generate_defines(lines, keycodes):
+ lines.append('')
+ lines.append('std::map<uint16_t, std::string> KEYCODE_ID_TABLE = {')
+ for key, value in keycodes["keycodes"].items():
+ lines.append(f' {{{value.get("key")}, "{value.get("key")}"}},')
+ lines.append('};')
+
+
+@cli.argument('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.')
+@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
+@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
+@cli.subcommand('Used by the make system to generate a keycode lookup table from keycodes_{version}.json', hidden=True)
+def generate_keycodes_tests(cli):
+ """Generates a keycode to identifier lookup table for unit test output.
+ """
+
+ # Build the keycodes.h file.
+ keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '// clang-format off']
+ keycodes_h_lines.append('extern "C" {\n#include <keycode.h>\n}')
+ keycodes_h_lines.append('#include <map>')
+ keycodes_h_lines.append('#include <string>')
+ keycodes_h_lines.append('#include <cstdint>')
+
+ keycodes = load_spec(cli.args.version)
+
+ _generate_defines(keycodes_h_lines, keycodes)
+
+ # Show the results
+ dump_lines(cli.args.output, keycodes_h_lines, cli.args.quiet)
diff --git a/lib/python/qmk/cli/generate/version_h.py b/lib/python/qmk/cli/generate/version_h.py
index a75702c529..fd87df3617 100644
--- a/lib/python/qmk/cli/generate/version_h.py
+++ b/lib/python/qmk/cli/generate/version_h.py
@@ -6,7 +6,7 @@ from milc import cli
from qmk.path import normpath
from qmk.commands import dump_lines
-from qmk.git import git_get_version
+from qmk.git import git_get_qmk_hash, git_get_version, git_is_dirty
from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
TIME_FMT = '%Y-%m-%d-%H:%M:%S'
@@ -29,23 +29,30 @@ def generate_version_h(cli):
current_time = strftime(TIME_FMT)
if cli.args.skip_git:
+ git_dirty = False
git_version = "NA"
+ git_qmk_hash = "NA"
chibios_version = "NA"
chibios_contrib_version = "NA"
else:
+ git_dirty = git_is_dirty()
git_version = git_get_version() or current_time
+ git_qmk_hash = git_get_qmk_hash() or "Unknown"
chibios_version = git_get_version("chibios", "os") or current_time
chibios_contrib_version = git_get_version("chibios-contrib", "os") or current_time
# Build the version.h file.
version_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once']
- version_h_lines.append(f"""
+ version_h_lines.append(
+ f"""
#define QMK_VERSION "{git_version}"
#define QMK_BUILDDATE "{current_time}"
+#define QMK_GIT_HASH "{git_qmk_hash}{'*' if git_dirty else ''}"
#define CHIBIOS_VERSION "{chibios_version}"
#define CHIBIOS_CONTRIB_VERSION "{chibios_contrib_version}"
-""")
+"""
+ )
# Show the results
dump_lines(cli.args.output, version_h_lines, cli.args.quiet)