summaryrefslogtreecommitdiffstats
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
-rw-r--r--lib/python/qmk/cli/generate/autocorrect_data.py82
-rwxr-xr-xlib/python/qmk/cli/generate/config_h.py2
-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.py67
-rw-r--r--lib/python/qmk/cli/generate/keycodes_tests.py39
-rw-r--r--lib/python/qmk/cli/generate/rgb_breathe_table.py49
-rw-r--r--lib/python/qmk/cli/generate/version_h.py13
9 files changed, 243 insertions, 110 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/autocorrect_data.py b/lib/python/qmk/cli/generate/autocorrect_data.py
index 00ab6180ab..5b70e0cb4e 100644
--- a/lib/python/qmk/cli/generate/autocorrect_data.py
+++ b/lib/python/qmk/cli/generate/autocorrect_data.py
@@ -33,9 +33,11 @@ from typing import Any, Dict, Iterator, List, Tuple
from milc import cli
-import qmk.path
+from qmk.commands import dump_lines
+from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.keymap import keymap_completer, locate_keymap
+from qmk.path import normpath
KC_A = 4
KC_SPC = 0x2c
@@ -63,9 +65,10 @@ def parse_file(file_name: str) -> List[Tuple[str, str]]:
try:
from english_words import english_words_lower_alpha_set as correct_words
except ImportError:
- cli.echo('Autocorrection will falsely trigger when a typo is a substring of a correctly spelled word.')
- cli.echo('To check for this, install the english_words package and rerun this script:')
- cli.echo(' {fg_cyan}python3 -m pip install english_words')
+ if not cli.args.quiet:
+ cli.echo('Autocorrection will falsely trigger when a typo is a substring of a correctly spelled word.')
+ cli.echo('To check for this, install the english_words package and rerun this script:')
+ cli.echo(' {fg_cyan}python3 -m pip install english_words')
# Use a minimal word list as a fallback.
correct_words = ('information', 'available', 'international', 'language', 'loosest', 'reference', 'wealthier', 'entertainment', 'association', 'provides', 'technology', 'statehood')
@@ -232,58 +235,51 @@ def encode_link(link: Dict[str, Any]) -> List[int]:
return [byte_offset & 255, byte_offset >> 8]
-def write_generated_code(autocorrections: List[Tuple[str, str]], data: List[int], file_name: str) -> None:
- """Writes autocorrection data as generated C code to `file_name`.
- Args:
- autocorrections: List of (typo, correction) tuples.
- data: List of ints in 0-255, the serialized trie.
- file_name: String, path of the output C file.
- """
- assert all(0 <= b <= 255 for b in data)
-
- def typo_len(e: Tuple[str, str]) -> int:
- return len(e[0])
+def typo_len(e: Tuple[str, str]) -> int:
+ return len(e[0])
- min_typo = min(autocorrections, key=typo_len)[0]
- max_typo = max(autocorrections, key=typo_len)[0]
- generated_code = ''.join([
- '// Generated code.\n\n', f'// Autocorrection dictionary ({len(autocorrections)} entries):\n', ''.join(sorted(f'// {typo:<{len(max_typo)}} -> {correction}\n' for typo, correction in autocorrections)),
- f'\n#define AUTOCORRECT_MIN_LENGTH {len(min_typo)} // "{min_typo}"\n', f'#define AUTOCORRECT_MAX_LENGTH {len(max_typo)} // "{max_typo}"\n\n', f'#define DICTIONARY_SIZE {len(data)}\n\n',
- textwrap.fill('static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {%s};' % (', '.join(map(str, data))), width=120, subsequent_indent=' '), '\n\n'
- ])
- with open(file_name, 'wt') as f:
- f.write(generated_code)
+def to_hex(b: int) -> str:
+ return f'0x{b:02X}'
-@cli.argument('filename', default='autocorrect_dict.txt', help='The autocorrection database file')
+@cli.argument('filename', type=normpath, help='The autocorrection database file')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
-@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
+@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('Generate the autocorrection data file from a dictionary file.')
def generate_autocorrect_data(cli):
autocorrections = parse_file(cli.args.filename)
trie = make_trie(autocorrections)
data = serialize_trie(autocorrections, trie)
- # Environment processing
- if cli.args.output == '-':
- cli.args.output = None
- if cli.args.output:
- cli.args.output.parent.mkdir(parents=True, exist_ok=True)
- cli.log.info('Creating autocorrect database at {fg_cyan}%s', cli.args.output)
- write_generated_code(autocorrections, data, cli.args.output)
+ current_keyboard = cli.args.keyboard or cli.config.user.keyboard or cli.config.generate_autocorrect_data.keyboard
+ current_keymap = cli.args.keymap or cli.config.user.keymap or cli.config.generate_autocorrect_data.keymap
+
+ if current_keyboard and current_keymap:
+ cli.args.output = locate_keymap(current_keyboard, current_keymap).parent / 'autocorrect_data.h'
- else:
- current_keyboard = cli.args.keyboard or cli.config.user.keyboard or cli.config.generate_autocorrect_data.keyboard
- current_keymap = cli.args.keymap or cli.config.user.keymap or cli.config.generate_autocorrect_data.keymap
+ assert all(0 <= b <= 255 for b in data)
- if current_keyboard and current_keymap:
- filename = locate_keymap(current_keyboard, current_keymap).parent / 'autocorrect_data.h'
- cli.log.info('Creating autocorrect database at {fg_cyan}%s', filename)
- write_generated_code(autocorrections, data, filename)
+ min_typo = min(autocorrections, key=typo_len)[0]
+ max_typo = max(autocorrections, key=typo_len)[0]
- else:
- write_generated_code(autocorrections, data, 'autocorrect_data.h')
+ # Build the autocorrect_data.h file.
+ autocorrect_data_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '']
- cli.log.info('Processed %d autocorrection entries to table with %d bytes.', len(autocorrections), len(data))
+ autocorrect_data_h_lines.append(f'// Autocorrection dictionary ({len(autocorrections)} entries):')
+ for typo, correction in autocorrections:
+ autocorrect_data_h_lines.append(f'// {typo:<{len(max_typo)}} -> {correction}')
+
+ autocorrect_data_h_lines.append('')
+ autocorrect_data_h_lines.append(f'#define AUTOCORRECT_MIN_LENGTH {len(min_typo)} // "{min_typo}"')
+ autocorrect_data_h_lines.append(f'#define AUTOCORRECT_MAX_LENGTH {len(max_typo)} // "{max_typo}"')
+ autocorrect_data_h_lines.append(f'#define DICTIONARY_SIZE {len(data)}')
+ autocorrect_data_h_lines.append('')
+ autocorrect_data_h_lines.append('static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {')
+ autocorrect_data_h_lines.append(textwrap.fill(' %s' % (', '.join(map(to_hex, data))), width=100, subsequent_indent=' '))
+ autocorrect_data_h_lines.append('};')
+
+ # Show the results
+ dump_lines(cli.args.output, autocorrect_data_h_lines, cli.args.quiet)
diff --git a/lib/python/qmk/cli/generate/config_h.py b/lib/python/qmk/cli/generate/config_h.py
index 31b8d70635..c256ec4531 100755
--- a/lib/python/qmk/cli/generate/config_h.py
+++ b/lib/python/qmk/cli/generate/config_h.py
@@ -62,7 +62,7 @@ def matrix_pins(matrix_pins, postfix=''):
def generate_matrix_size(kb_info_json, config_h_lines):
"""Add the matrix size to the config.h.
"""
- if 'matrix_pins' in kb_info_json:
+ if 'matrix_size' in kb_info_json:
config_h_lines.append(generate_define('MATRIX_COLS', kb_info_json['matrix_size']['cols']))
config_h_lines.append(generate_define('MATRIX_ROWS', kb_info_json['matrix_size']['rows']))
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..17503bac63 100644
--- a/lib/python/qmk/cli/generate/keycodes.py
+++ b/lib/python/qmk/cli/generate/keycodes.py
@@ -8,6 +8,34 @@ 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'
+ if group == 'media':
+ return 'consumer'
+ 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 +92,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 +131,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/rgb_breathe_table.py b/lib/python/qmk/cli/generate/rgb_breathe_table.py
index 8cf83238e1..55c80f6015 100644
--- a/lib/python/qmk/cli/generate/rgb_breathe_table.py
+++ b/lib/python/qmk/cli/generate/rgb_breathe_table.py
@@ -5,7 +5,9 @@ from argparse import ArgumentTypeError
from milc import cli
-import qmk.path
+from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
+from qmk.commands import dump_lines
+from qmk.path import normpath
def breathing_center(value):
@@ -24,17 +26,10 @@ def breathing_max(value):
raise ArgumentTypeError('Breathing max must be between 0 and 255')
-@cli.argument('-c', '--center', arg_only=True, type=breathing_center, default=1.85, help='The breathing center value, from 1 to 2.7. Default: 1.85')
-@cli.argument('-m', '--max', arg_only=True, type=breathing_max, default=255, help='The breathing maximum value, from 0 to 255. Default: 255')
-@cli.argument('-o', '--output', arg_only=True, type=qmk.path.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('Generates an RGB Light breathing table header.')
-def generate_rgb_breathe_table(cli):
- """Generate a rgblight_breathe_table.h file containing a breathing LUT for RGB Lighting (Underglow) feature.
- """
+def _generate_table(lines, center, maximum):
breathe_values = [0] * 256
for pos in range(0, 256):
- breathe_values[pos] = (int)((math.exp(math.sin((pos / 255) * math.pi)) - cli.args.center / math.e) * (cli.args.max / (math.e - 1 / math.e)))
+ breathe_values[pos] = (int)((math.exp(math.sin((pos / 255) * math.pi)) - center / math.e) * (maximum / (math.e - 1 / math.e)))
values_template = ''
for s in range(0, 3):
@@ -51,11 +46,7 @@ def generate_rgb_breathe_table(cli):
values_template += '#endif'
values_template += '\n\n' if s < 2 else ''
- table_template = '''#pragma once
-
-#define RGBLIGHT_EFFECT_BREATHE_TABLE
-
-// clang-format off
+ table_template = '''#define RGBLIGHT_EFFECT_BREATHE_TABLE
// Breathing center: {0:.2f}
// Breathing max: {1:d}
@@ -65,15 +56,23 @@ const uint8_t PROGMEM rgblight_effect_breathe_table[] = {{
}};
static const int table_scale = 256 / sizeof(rgblight_effect_breathe_table);
-'''.format(cli.args.center, cli.args.max, values_template)
+'''.format(center, maximum, values_template)
+ lines.append(table_template)
- if cli.args.output:
- cli.args.output.parent.mkdir(parents=True, exist_ok=True)
- if cli.args.output.exists():
- cli.args.output.replace(cli.args.output.parent / (cli.args.output.name + '.bak'))
- cli.args.output.write_text(table_template)
- if not cli.args.quiet:
- cli.log.info('Wrote header to %s.', cli.args.output)
- else:
- print(table_template)
+@cli.argument('-c', '--center', arg_only=True, type=breathing_center, default=1.85, help='The breathing center value, from 1 to 2.7. Default: 1.85')
+@cli.argument('-m', '--max', arg_only=True, type=breathing_max, default=255, help='The breathing maximum value, from 0 to 255. Default: 255')
+@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('Generates an RGB Light breathing table header.')
+def generate_rgb_breathe_table(cli):
+ """Generate a rgblight_breathe_table.h file containing a breathing LUT for RGB Lighting (Underglow) feature.
+ """
+
+ # Build the header file.
+ header_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off']
+
+ _generate_table(header_lines, cli.args.center, cli.args.max)
+
+ # Show the results
+ dump_lines(cli.args.output, header_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)