From 4c0d8ae8d77a76857abf4befc13d8f6c969c52e0 Mon Sep 17 00:00:00 2001 From: Zach White Date: Fri, 21 May 2021 16:59:10 -0700 Subject: ensure we do not conflict with existing keymap aliases (#12976) --- lib/python/qmk/cli/generate/layouts.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/python/qmk/cli/generate') diff --git a/lib/python/qmk/cli/generate/layouts.py b/lib/python/qmk/cli/generate/layouts.py index ad6946d6cf..4de982f822 100755 --- a/lib/python/qmk/cli/generate/layouts.py +++ b/lib/python/qmk/cli/generate/layouts.py @@ -85,7 +85,9 @@ def generate_layouts(cli): for alias, target in kb_info_json.get('layout_aliases', {}).items(): layouts_h_lines.append('') - layouts_h_lines.append('#define %s %s' % (alias, target)) + layouts_h_lines.append(f'#ifndef {alias}') + layouts_h_lines.append(f'# define {alias} {target}') + layouts_h_lines.append('#endif') # Show the results layouts_h = '\n'.join(layouts_h_lines) + '\n' -- cgit v1.2.3 From bbe43a91ebf193bbc8c09ba59209b0524367e68c Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 27 Jun 2021 02:29:02 +1000 Subject: CLI: Add subcommand to generate version.h (#13151) --- Makefile | 27 +++----------- lib/python/qmk/cli/__init__.py | 1 + lib/python/qmk/cli/generate/version_h.py | 28 ++++++++++++++ lib/python/qmk/commands.py | 62 +++++++++++++++++++------------ lib/python/qmk/tests/test_cli_commands.py | 6 +++ 5 files changed, 79 insertions(+), 45 deletions(-) create mode 100644 lib/python/qmk/cli/generate/version_h.py (limited to 'lib/python/qmk/cli/generate') diff --git a/Makefile b/Makefile index e007ae3679..866bcaaf3f 100644 --- a/Makefile +++ b/Makefile @@ -548,29 +548,14 @@ git-submodule: git submodule sync --recursive git submodule update --init --recursive --progress -ifdef SKIP_VERSION -SKIP_GIT := yes -endif - # Generate the version.h file -ifndef SKIP_GIT - GIT_VERSION := $(shell git describe --abbrev=6 --dirty --always --tags 2>/dev/null || date +"%Y-%m-%d-%H:%M:%S") - CHIBIOS_VERSION := $(shell cd lib/chibios && git describe --abbrev=6 --dirty --always --tags 2>/dev/null || date +"%Y-%m-%d-%H:%M:%S") - CHIBIOS_CONTRIB_VERSION := $(shell cd lib/chibios-contrib && git describe --abbrev=6 --dirty --always --tags 2>/dev/null || date +"%Y-%m-%d-%H:%M:%S") -else - GIT_VERSION := NA - CHIBIOS_VERSION := NA - CHIBIOS_CONTRIB_VERSION := NA +ifdef SKIP_GIT +VERSION_H_FLAGS := --skip-git endif -ifndef SKIP_VERSION -BUILD_DATE := $(shell date +"%Y-%m-%d-%H:%M:%S") -else -BUILD_DATE := 2020-01-01-00:00:00 +ifdef SKIP_VERSION +VERSION_H_FLAGS := --skip-all +SKIP_GIT := yes endif - -$(shell echo '#define QMK_VERSION "$(GIT_VERSION)"' > $(ROOT_DIR)/quantum/version.h) -$(shell echo '#define QMK_BUILDDATE "$(BUILD_DATE)"' >> $(ROOT_DIR)/quantum/version.h) -$(shell echo '#define CHIBIOS_VERSION "$(CHIBIOS_VERSION)"' >> $(ROOT_DIR)/quantum/version.h) -$(shell echo '#define CHIBIOS_CONTRIB_VERSION "$(CHIBIOS_CONTRIB_VERSION)"' >> $(ROOT_DIR)/quantum/version.h) +$(shell $(QMK_BIN) generate-version-h $(VERSION_H_FLAGS) -q -o quantum/version.h) include $(ROOT_DIR)/testlist.mk diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 7f5e0a1fa6..2c3c9c4211 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -49,6 +49,7 @@ subcommands = [ 'qmk.cli.generate.layouts', 'qmk.cli.generate.rgb_breathe_table', 'qmk.cli.generate.rules_mk', + 'qmk.cli.generate.version_h', 'qmk.cli.hello', 'qmk.cli.info', 'qmk.cli.json2c', diff --git a/lib/python/qmk/cli/generate/version_h.py b/lib/python/qmk/cli/generate/version_h.py new file mode 100644 index 0000000000..b8e52588c4 --- /dev/null +++ b/lib/python/qmk/cli/generate/version_h.py @@ -0,0 +1,28 @@ +"""Used by the make system to generate version.h for use in code. +""" +from milc import cli + +from qmk.commands import create_version_h +from qmk.path import normpath + + +@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.argument('--skip-git', arg_only=True, action='store_true', help='Skip Git operations') +@cli.argument('--skip-all', arg_only=True, action='store_true', help='Use placeholder values for all defines (implies --skip-git)') +@cli.subcommand('Used by the make system to generate version.h for use in code', hidden=True) +def generate_version_h(cli): + """Generates the version.h file. + """ + if cli.args.skip_all: + cli.args.skip_git = True + + version_h = create_version_h(cli.args.skip_git, cli.args.skip_all) + + if cli.args.output: + cli.args.output.write_text(version_h) + + if not cli.args.quiet: + cli.log.info('Wrote version.h to %s.', cli.args.output) + else: + print(version_h) diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py index 3a35c11031..104c875457 100644 --- a/lib/python/qmk/commands.py +++ b/lib/python/qmk/commands.py @@ -86,11 +86,17 @@ def create_make_command(keyboard, keymap, target=None, parallel=1, **env_vars): return create_make_target(':'.join(make_args), parallel, **env_vars) -def get_git_version(repo_dir='.', check_dir='.'): +def get_git_version(current_time, repo_dir='.', check_dir='.'): """Returns the current git version for a repo, or the current time. """ git_describe_cmd = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags'] + if repo_dir != '.': + repo_dir = Path('lib') / repo_dir + + if check_dir != '.': + check_dir = repo_dir / check_dir + if Path(check_dir).exists(): git_describe = cli.run(git_describe_cmd, stdin=DEVNULL, cwd=repo_dir) @@ -100,23 +106,40 @@ def get_git_version(repo_dir='.', check_dir='.'): else: cli.log.warn(f'"{" ".join(git_describe_cmd)}" returned error code {git_describe.returncode}') print(git_describe.stderr) - return strftime(time_fmt) + return current_time - return strftime(time_fmt) + return current_time -def write_version_h(git_version, build_date, chibios_version, chibios_contrib_version): - """Generate and write quantum/version.h +def create_version_h(skip_git=False, skip_all=False): + """Generate version.h contents """ - version_h = [ - f'#define QMK_VERSION "{git_version}"', - f'#define QMK_BUILDDATE "{build_date}"', - f'#define CHIBIOS_VERSION "{chibios_version}"', - f'#define CHIBIOS_CONTRIB_VERSION "{chibios_contrib_version}"', - ] + if skip_all: + current_time = "1970-01-01-00:00:00" + else: + current_time = strftime(time_fmt) + + if skip_git: + git_version = "NA" + chibios_version = "NA" + chibios_contrib_version = "NA" + else: + git_version = get_git_version(current_time) + chibios_version = get_git_version(current_time, "chibios", "os") + chibios_contrib_version = get_git_version(current_time, "chibios-contrib", "os") + + version_h_lines = f"""/* This file was automatically generated. Do not edit or copy. + */ + +#pragma once + +#define QMK_VERSION "{git_version}" +#define QMK_BUILDDATE "{current_time}" +#define CHIBIOS_VERSION "{chibios_version}" +#define CHIBIOS_CONTRIB_VERSION "{chibios_contrib_version}" +""" - version_h_file = Path('quantum/version.h') - version_h_file.write_text('\n'.join(version_h)) + return version_h_lines def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_vars): @@ -149,13 +172,8 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va keymap_dir.mkdir(exist_ok=True, parents=True) keymap_c.write_text(c_text) - # Write the version.h file - git_version = get_git_version() - build_date = strftime('%Y-%m-%d-%H:%M:%S') - chibios_version = get_git_version("lib/chibios", "lib/chibios/os") - chibios_contrib_version = get_git_version("lib/chibios-contrib", "lib/chibios-contrib/os") - - write_version_h(git_version, build_date, chibios_version, chibios_contrib_version) + version_h = Path('quantum/version.h') + version_h.write_text(create_version_h()) # Return a command that can be run to make the keymap and flash if given verbose = 'true' if cli.config.general.verbose else 'false' @@ -181,10 +199,6 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va make_command.append(f'{key}={value}') make_command.extend([ - f'GIT_VERSION={git_version}', - f'BUILD_DATE={build_date}', - f'CHIBIOS_VERSION={chibios_version}', - f'CHIBIOS_CONTRIB_VERSION={chibios_contrib_version}', f'KEYBOARD={user_keymap["keyboard"]}', f'KEYMAP={user_keymap["keymap"]}', f'KEYBOARD_FILESAFE={keyboard_filesafe}', diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py index afdbc81429..b341e1c912 100644 --- a/lib/python/qmk/tests/test_cli_commands.py +++ b/lib/python/qmk/tests/test_cli_commands.py @@ -258,6 +258,12 @@ def test_generate_rules_mk(): assert 'MCU ?= atmega32u4' in result.stdout +def test_generate_version_h(): + result = check_subcommand('generate-version-h') + check_returncode(result) + assert '#define QMK_VERSION' in result.stdout + + def test_generate_layouts(): result = check_subcommand('generate-layouts', '-kb', 'handwired/pytest/basic') check_returncode(result) -- cgit v1.2.3 From 8d9bfdc25437bb401985ba93b47edae2126e7fac Mon Sep 17 00:00:00 2001 From: Zach White Date: Mon, 16 Aug 2021 15:33:30 -0700 Subject: Add a lot more data to info.json (#13366) * add some split data to info.json * add tags * add half of config_options.md to info.json * add support for designating master split * sort out split transport and primary * fix bad data in UNUSED_PINS * fixup custom transport * wip * allow for setting split right half keyboard matrix * add SPLIT_USB_DETECT * minor cleanup * fix an erroneous message * rework split.usb_detect * adding missing rgblight vars to info.json * add mouse_key to info.json * add all remaining options from docs/config_options.md * fix audio voices * qmk info: Change text output to use dotted notation * tweak layout output * resolve alias names * break out some functions to make flake8 happy * add a field for bootloader instructions * qmk generate-info-json: add a write-to-file argument Adds an argument that instructs qmk generate-info-json to write the output to a file instead of just to the terminal. * -arg_only, +action Because it was never my intention that one would have to specify a value for the argument that enables writing the file. * Bring qmk generate-info-json inline with other generate commands * pytest fixup * fix esca/getawayvan * fix data driven errors for bpiphany converters * features.force_nkro -> usb.force_nkro * split.primary->split.main * fix esca/getawayvan_f042 * fix the bpiphany converters for real * fix bpiphany/tiger_lily * Apply suggestions from code review Co-authored-by: Nick Brassel * fix generate-api errors * fix matrix pin extraction for split boards * fix ploopyco/trackball_nano/rev1_001 Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com> Co-authored-by: Nick Brassel --- data/mappings/info_config.json | 56 +++++- data/mappings/info_rules.json | 14 +- data/schemas/keyboard.jsonschema | 220 ++++++++++++++++++++- keyboards/bpiphany/frosty_flake/config.h | 5 + keyboards/bpiphany/tiger_lily/config.h | 5 + keyboards/cest73/tkm/config.h | 2 +- keyboards/esca/getawayvan/config.h | 1 - keyboards/esca/getawayvan_f042/config.h | 1 - keyboards/handwired/tennie/config.h | 2 +- keyboards/mesa/mesa_tkl/config.h | 2 +- .../ploopyco/trackball_nano/rev1_001/config.h | 2 +- lib/python/qmk/cli/generate/config_h.py | 140 ++++++++----- lib/python/qmk/cli/generate/info_json.py | 40 +++- lib/python/qmk/cli/generate/rules_mk.py | 11 ++ lib/python/qmk/cli/info.py | 53 +++-- lib/python/qmk/info.py | 184 ++++++++++++++++- lib/python/qmk/json_schema.py | 15 +- 17 files changed, 654 insertions(+), 99 deletions(-) (limited to 'lib/python/qmk/cli/generate') diff --git a/data/mappings/info_config.json b/data/mappings/info_config.json index b949b13320..18477f9129 100644 --- a/data/mappings/info_config.json +++ b/data/mappings/info_config.json @@ -1,21 +1,46 @@ -# This file maps keys between `config.h` and `info.json`. It is used by QMK +# This file maps keys between `config.h` and `info.json`. It is used by QMK # to correctly and consistently map back and forth between the two systems. { # Format: # : {"info_key": , ["value_type": ], ["to_json": ], ["to_c": ]} - # value_type: one of "array", "array.int", "int", "hex", "list", "mapping" + # value_type: one of "array", "array.int", "bool", "int", "hex", "list", "mapping" # to_json: Default `true`. Set to `false` to exclude this mapping from info.json # to_c: Default `true`. Set to `false` to exclude this mapping from config.h # warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places - "DEBOUNCE": {"info_key": "debounce", "value_type": "int"} + "AUDIO_VOICES": {"info_key": "audio.voices", "value_type": "bool"}, + "BACKLIGHT_BREATHING": {"info_key": "backlight.breathing", "value_type": "bool"}, + "BREATHING_PERIOD": {"info_key": "backlight.breathing_period", "value_type": "int"}, + "BACKLIGHT_PIN": {"info_key": "backlight.pin"}, + "COMBO_COUNT": {"info_key": "combo.count", "value_type": "int"}, + "COMBO_TERM": {"info_key": "combo.term", "value_type": "int"}, + "DEBOUNCE": {"info_key": "debounce", "value_type": "int"}, "DEVICE_VER": {"info_key": "usb.device_ver", "value_type": "hex"}, "DESCRIPTION": {"info_key": "keyboard_folder", "to_json": false}, "DIODE_DIRECTION": {"info_key": "diode_direction"}, + "FORCE_NKRO": {"info_key": "usb.force_nkro", "value_type": "bool"}, + "IGNORE_MOD_TAP_INTERRUPT": {"info_key": "tapping.ignore_mod_tap_interrupt", "value_type": "bool"}, + "IGNORE_MOD_TAP_INTERRUPT_PER_KEY": {"info_key": "tapping.ignore_mod_tap_interrupt_per_key", "value_type": "bool"}, "LAYOUTS": {"info_key": "layout_aliases", "value_type": "mapping"}, + "LEADER_PER_KEY_TIMING": {"info_key": "leader_key.timing", "value_type": "bool"}, + "LEADER_KEY_STRICT_KEY_PROCESSING": {"info_key": "leader_key.strict_processing", "value_type": "bool"}, + "LEADER_TIMEOUT": {"info_key": "leader_key.timeout", "value_type": "int"}, "LED_CAPS_LOCK_PIN": {"info_key": "indicators.caps_lock"}, "LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"}, "LED_SCROLL_LOCK_PIN": {"info_key": "indicators.scroll_lock"}, "MANUFACTURER": {"info_key": "manufacturer"}, + "MATRIX_HAS_GHOST": {"info_key": "matrix_pins.ghost", "value_type": "bool"}, + "MATRIX_IO_DELAY": {"info_key": "matrix_pins.io_delay", "value_type": "int"}, + "MOUSEKEY_DELAY": {"info_key": "mousekey.delay", "value_type": "int"}, + "MOUSEKEY_INTERVAL": {"info_key": "mousekey.interval", "value_type": "int"}, + "MOUSEKEY_MAX_SPEED": {"info_key": "mousekey.max_speed", "value_type": "int"}, + "MOUSEKEY_TIME_TO_MAX": {"info_key": "mousekey.time_to_max", "value_type": "int"}, + "MOUSEKEY_WHEEL_DELAY": {"info_key": "mousekey.wheel_delay", "value_type": "int"}, + "ONESHOT_TIMEOUT": {"info_key": "oneshot.timeout", "value_type": "int"}, + "ONESHOT_TAP_TOGGLE": {"info_key": "oneshot.tap_toggle", "value_type": "int"}, + "PERMISSIVE_HOLD": {"info_key": "tapping.permissive_hold", "value_type": "bool"}, + "PERMISSIVE_HOLD_PER_KEY": {"info_key": "tapping.permissive_hold_per_key", "value_type": "bool"}, + "RETRO_TAPPING": {"info_key": "tapping.retro", "value_type": "bool"}, + "RETRO_TAPPING_PER_KEY": {"info_key": "tapping.retro_per_key", "value_type": "bool"}, "RGB_DI_PIN": {"info_key": "rgblight.pin"}, "RGBLED_NUM": {"info_key": "rgblight.led_count", "value_type": "int"}, "RGBLED_SPLIT": {"info_key": "rgblight.split_count", "value_type": "array.int"}, @@ -30,17 +55,40 @@ "RGBLIGHT_EFFECT_SNAKE": {"info_key": "rgblight.animations.snake", "value_type": "bool"}, "RGBLIGHT_EFFECT_STATIC_GRADIENT": {"info_key": "rgblight.animations.static_gradient", "value_type": "bool"}, "RGBLIGHT_EFFECT_TWINKLE": {"info_key": "rgblight.animations.twinkle"}, + "RGBLIGHT_LAYER_BLINK": {"info_key": "rgblight.layers.blink", "value_type": "bool"}, + "RGBLIGHT_LAYERS": {"info_key": "rgblight.layers.enabled", "value_type": "bool"}, + "RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF": {"info_key": "rgblight.layers.override_rgb", "value_type": "bool"}, "RGBLIGHT_LIMIT_VAL": {"info_key": "rgblight.max_brightness", "value_type": "int"}, + "RGBLIGHT_MAX_LAYERS": {"info_key": "rgblight.layers.max", "value_type": "int"}, "RGBLIGHT_HUE_STEP": {"info_key": "rgblight.hue_steps", "value_type": "int"}, "RGBLIGHT_SAT_STEP": {"info_key": "rgblight.saturation_steps", "value_type": "int"}, "RGBLIGHT_VAL_STEP": {"info_key": "rgblight.brightness_steps", "value_type": "int"}, "RGBLIGHT_SLEEP": {"info_key": "rgblight.sleep", "value_type": "bool"}, "RGBLIGHT_SPLIT": {"info_key": "rgblight.split", "value_type": "bool"}, + "RGBW": {"info_key": "rgblight.rgbw", "value_type": "bool"}, "PRODUCT": {"info_key": "keyboard_folder", "to_json": false}, "PRODUCT_ID": {"info_key": "usb.pid", "value_type": "hex"}, "VENDOR_ID": {"info_key": "usb.vid", "value_type": "hex"}, "QMK_ESC_OUTPUT": {"info_key": "qmk_lufa_bootloader.esc_output"}, "QMK_ESC_INPUT": {"info_key": "qmk_lufa_bootloader.esc_input"}, + "QMK_KEYS_PER_SCAN": {"info_key": "qmk.keys_per_scan", "value_type": "int"}, "QMK_LED": {"info_key": "qmk_lufa_bootloader.led"}, - "QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"} + "QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"}, + "SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync_modifiers", "value_type": "bool"}, + "SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync_matrix_state", "value_type": "bool"}, + "SPLIT_USB_DETECT": {"info_key": "split.usb_detect.enabled", "value_type": "bool"}, + "SPLIT_USB_TIMEOUT": {"info_key": "split.usb_detect.timeout", "value_type": "int"}, + "SPLIT_USB_TIMEOUT_POLL": {"info_key": "split.usb_detect.polling_interval", "value_type": "int"}, + "SOFT_SERIAL_PIN": {"info_key": "split.soft_serial_pin"}, + "SOFT_SERIAL_SPEED": {"info_key": "split.soft_serial_speed"}, + "TAP_CODE_DELAY": {"info_key": "qmk.tap_keycode_delay", "value_type": "int"}, + "TAP_HOLD_CAPS_DELAY": {"info_key": "qmk.tap_capslock_delay", "value_type": "int"}, + "TAPPING_FORCE_HOLD": {"info_key": "tapping.force_hold", "value_type": "bool"}, + "TAPPING_FORCE_HOLD_PER_KEY": {"info_key": "tapping.force_hold_per_key", "value_type": "bool"}, + "TAPPING_TERM": {"info_key": "tapping.term", "value_type": "int"}, + "TAPPING_TERM_PER_KEY": {"info_key": "tapping.term_per_key", "value_type": "bool"}, + "TAPPING_TOGGLE": {"info_key": "tapping.toggle", "value_type": "int"}, + "USB_MAX_POWER_CONSUMPTION": {"info_key": "usb.max_power", "value_type": "int"}, + "USB_POLLING_INTERVAL_MS": {"info_key": "usb.polling_interval", "value_type": "int"}, + "USB_SUSPEND_WAKEUP_DELAY": {"info_key": "usb.suspend_wakeup_delay", "value_type": "int"}, } diff --git a/data/mappings/info_rules.json b/data/mappings/info_rules.json index 97f772c4d5..aea67e04c8 100644 --- a/data/mappings/info_rules.json +++ b/data/mappings/info_rules.json @@ -1,15 +1,25 @@ -# This file maps keys between `rules.mk` and `info.json`. It is used by QMK +# This file maps keys between `rules.mk` and `info.json`. It is used by QMK # to correctly and consistently map back and forth between the two systems. { # Format: # : {"info_key": , ["value_type": ], ["to_json": ], ["to_c": ]} - # value_type: one of "array", "array.int", "int", "list", "hex", "mapping" + # value_type: one of "array", "array.int", "bool", "int", "list", "hex", "mapping" # to_json: Default `true`. Set to `false` to exclude this mapping from info.json # to_c: Default `true`. Set to `false` to exclude this mapping from rules.mk # warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places "BOARD": {"info_key": "board"}, "BOOTLOADER": {"info_key": "bootloader", "warn_duplicate": false}, + "BLUETOOTH": {"info_key": "bluetooth.driver"}, + "FIRMWARE_FORMAT": {"info_key": "build.firmware_format"}, + "KEYBOARD_SHARED_EP": {"info_key": "usb.shared_endpoint.keyboard", "value_type": "bool"}, + "MOUSE_SHARED_EP": {"info_key": "usb.shared_endpoint.mouse", "value_type": "bool"}, "LAYOUTS": {"info_key": "community_layouts", "value_type": "list"}, "LED_MATRIX_DRIVER": {"info_key": "led_matrix.driver"}, + "LTO_ENABLE": {"info_key": "build.lto", "value_type": "bool"}, "MCU": {"info_key": "processor", "warn_duplicate": false}, + "MOUSEKEY_ENABLE": {"info_key": "mouse_key.enabled", "value_type": "bool"}, + "NO_USB_STARTUP_CHECK": {"info_key": "usb.no_startup_check", "value_type": "bool"}, + "SPLIT_KEYBOARD": {"info_key": "split.enabled", "value_type": "bool"}, + "SPLIT_TRANSPORT": {"info_key": "split.transport.protocol", "value_type": "str", "to_c": false}, + "WAIT_FOR_USB": {"info_key": "usb.wait_for", "value_type": "bool"} } diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index 3a3fa4b738..78c9a8d36b 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -15,6 +15,40 @@ "type": "string", "enum": ["cortex-m0", "cortex-m0plus", "cortex-m3", "cortex-m4", "MKL26Z64", "MK20DX128", "MK20DX256", "MK66F18", "STM32F042", "STM32F072", "STM32F103", "STM32F303", "STM32F401", "STM32F407", "STM32F411", "STM32F446", "STM32G431", "STM32G474", "STM32L412", "STM32L422", "STM32L433", "STM32L443", "atmega16u2", "atmega32u2", "atmega16u4", "atmega32u4", "at90usb162", "at90usb646", "at90usb647", "at90usb1286", "at90usb1287", "atmega32a", "atmega328p", "atmega328", "attiny85", "unknown"] }, + "audio": { + "type": "object", + "additionalProperties": false, + "properties": { + "pins": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}, + "voices": {"type": "boolean"} + } + }, + "backlight": { + "type": "object", + "additionalProperties": false, + "properties": { + "breathing": {"type": "boolean"}, + "breathing_period": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, + "levels": { + "type": "number", + "min": 1, + "max": 31, + "multipleOf": 1 + }, + "pin": {"$ref": "qmk.definitions.v1#/mcu_pin"} + } + }, + "bluetooth": { + "type": "object", + "additionalProperties": false, + "properties": { + "driver": { + "type": "string", + "enum": ["AdafruitBLE", "RN42"] + }, + "lto": {"type": "boolean"}, + } + }, "board": { "type": "string", "minLength": 2, @@ -22,13 +56,39 @@ }, "bootloader": { "type": "string", - "enum": ["atmel-dfu", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "micronucleus", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "unknown", "USBasp", "tinyuf2"] + "enum": ["atmel-dfu", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "micronucleus", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "unknown", "USBasp", "tinyuf2"], + }, + "bootloader_instructions": { + "type": "string", + "description": "Instructions for putting the keyboard into a mode that allows for firmware flashing." + }, + "build": { + "type": "object", + "additionalProperties": false, + "properties": { + "debounce_type": { + "type": "string", + "enum": ["custom", "eager_pk", "eager_pr", "sym_defer_pk", "sym_eager_pk"] + }, + "firmware_format": { + "type": "string", + "enum": ["bin", "hex", "uf2"] + }, + "lto": {"type": "boolean"}, + } }, "diode_direction": { "type": "string", "enum": ["COL2ROW", "ROW2COL"] }, "debounce": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "combo": { + "type": "object", + "properties": { + "count": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "term": {"$ref": "qmk.definitions.v1#/unsigned_int"} + } + }, "community_layouts": { "type": "array", "items": {"$ref": "qmk.definitions.v1#/filename"} @@ -90,16 +150,47 @@ } } }, + "leader_key": { + "type": "object", + "properties": { + "timing": {"type": "boolean"}, + "strict_processing": {"type": "boolean"}, + "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"} + } + }, "matrix_pins": { "type": "object", "additionalProperties": false, "properties": { + "custom": {"type": "boolean"}, + "custom_lite": {"type": "boolean"}, + "ghost": {"type": "boolean"}, + "io_delay": {"$ref": "qmk.definitions.v1#/unsigned_int"}, "direct": { "type": "array", - "items": {$ref": "qmk.definitions.v1#/mcu_pin_array"} + "items": {"$ref": "qmk.definitions.v1#/mcu_pin_array"} }, "cols": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}, - "rows": {"$ref": "qmk.definitions.v1#/mcu_pin_array"} + "rows": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}, + "unused": {"$ref": "qmk.definitions.v1#/mcu_pin_array"} + } + }, + "mouse_key": { + "type": "object", + "properties": { + "enabled": {"type": "boolean"}, + "delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"} + "interval": {"$ref": "qmk.definitions.v1#/unsigned_int_8"} + "max_speed": {"$ref": "qmk.definitions.v1#/unsigned_int_8"} + "time_to_max": {"$ref": "qmk.definitions.v1#/unsigned_int_8"} + "wheel_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"} + } + }, + "oneshot": { + "type": "object", + "properties": { + "tap_toggle": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"} } }, "rgblight": { @@ -114,9 +205,25 @@ }, "brightness_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"}, "hue_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "layers": { + "type": "object", + "additionalProperties": false, + "properties": { + "blink": {"type": "boolean"}, + "enabled": {"type": "boolean"}, + "max": { + "type": "number", + "min": 1, + "max": 32, + "multipleOf": 1 + }, + "override_rgb": {"type": "boolean"} + } + }, "led_count": {"$ref": "qmk.definitions.v1#/unsigned_int"}, "max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, "pin": {"$ref": "qmk.definitions.v1#/mcu_pin"}, + "rgbw": {"type": "boolean"}, "saturation_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"}, "sleep": {"type": "boolean"}, "split": {"type": "boolean"}, @@ -128,13 +235,118 @@ } } }, + "split": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": {"type": "boolean"}, + "matrix_grid": { + "type": "array", + "items": {"$ref": "qmk.definitions.v1#/mcu_pin"} + }, + "matrix_pins": { + "type": "object", + "additionalProperties": false, + "properties": { + "right": { + "type": "object", + "additionalProperties": false, + "properties": { + "direct": { + "type": "array", + "items": {"$ref": "qmk.definitions.v1#/mcu_pin_array"} + }, + "cols": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}, + "rows": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}, + "unused": {"$ref": "qmk.definitions.v1#/mcu_pin_array"} + } + } + } + }, + "main": { + "type": "string", + "enum": ["eeprom", "left", "matrix_grid", "pin", "right"] + }, + "soft_serial_pin": {"$ref": "qmk.definitions.v1#/mcu_pin"}, + "soft_serial_speed": { + "type": "number", + "min": 0, + "max": 5, + "multipleOf": 1 + }, + "transport": { + "type": "object", + "additionalProperties": false, + "properties": { + "protocol": { + "type": "string", + "enum": ["custom", "i2c", "serial", "serial_usart"] + }, + "sync_matrix_state": {"type": "boolean"}, + "sync_modifiers": {"type": "boolean"} + } + }, + "usb_detect": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": {"type": "boolean"}, + "polling_interval": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"} + } + } + } + }, + "tags": { + "type": "array", + "items": {"type": "string"} + }, + "tapping": { + "type": "object", + "properties": { + "force_hold": {"type": "boolean"}, + "force_hold_per_key": {"type": "boolean"}, + "ignore_mod_tap_interrupt": {"type": "boolean"}, + "ignore_mod_tap_interrupt_per_key": {"type": "boolean"}, + "permissive_hold": {"type": "boolean"}, + "permissive_hold_per_key": {"type": "boolean"}, + "retro": {"type": "boolean"}, + "retro_per_key": {"type": "boolean"}, + "term": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "term_per_key": {"type": "boolean"}, + "toggle": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + } + }, "usb": { "type": "object", "additionalProperties": false, "properties": { "device_ver": {"$ref": "qmk.definitions.v1#/hex_number_4d"}, + "force_nkro": {"type": "boolean"}, "pid": {"$ref": "qmk.definitions.v1#/hex_number_4d"}, - "vid": {"$ref": "qmk.definitions.v1#/hex_number_4d"} + "vid": {"$ref": "qmk.definitions.v1#/hex_number_4d"}, + "max_power": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, + "no_startup_check": {"type": "boolean"}, + "polling_interval": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, + "shared_endpoint": { + "type": "object", + "additionalProperties": false, + "properties": { + "keyboard": {"type": "boolean"}, + "mouse": {"type": "boolean"} + } + }, + "suspend_wakeup_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, + "wait_for": {"type": "boolean"}, + } + }, + "qmk": { + "type": "object", + "additionalProperties": false, + "properties": { + "keys_per_scan": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, + "tap_keycode_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, + "tap_capslock_delay": {"$ref": "qmk.definitions.v1#/unsigned_int_8"}, } }, "qmk_lufa_bootloader": { diff --git a/keyboards/bpiphany/frosty_flake/config.h b/keyboards/bpiphany/frosty_flake/config.h index 344db5f0d9..ae19184a6a 100644 --- a/keyboards/bpiphany/frosty_flake/config.h +++ b/keyboards/bpiphany/frosty_flake/config.h @@ -40,7 +40,12 @@ along with this program. If not, see . /* * Keyboard Matrix Assignments + * + * MATRIX_ROW_PINS and MATRIX_COL_PINS aren't actually used and are included + * for data driven compatibility. */ +#define MATRIX_COL_PINS { B0, B3, B2, B1, B6, B4, B5, C7 } +#define MATRIX_ROW_PINS { NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN } #define UNUSED_PINS { C0, C1, C2, C3, C4, D2, D7 } /* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */ diff --git a/keyboards/bpiphany/tiger_lily/config.h b/keyboards/bpiphany/tiger_lily/config.h index a86cffd16b..d3e2464bee 100644 --- a/keyboards/bpiphany/tiger_lily/config.h +++ b/keyboards/bpiphany/tiger_lily/config.h @@ -40,7 +40,12 @@ along with this program. If not, see . /* * Keyboard Matrix Assignments + * + * MATRIX_ROW_PINS and MATRIX_COL_PINS aren't actually used and are included + * for data driven compatibility. */ +#define MATRIX_ROW_PINS { C2, B3, B4, B2, B1, C7, B6, B5 } +#define MATRIX_COL_PINS { NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN } #define UNUSED_PINS { B0, C4, D3 } #define LED_NUM_LOCK_PIN C5 diff --git a/keyboards/cest73/tkm/config.h b/keyboards/cest73/tkm/config.h index deef979b2c..e925f39715 100644 --- a/keyboards/cest73/tkm/config.h +++ b/keyboards/cest73/tkm/config.h @@ -34,7 +34,7 @@ #define MATRIX_ROW_PINS { B0, B1, B2, B3, B4, B5, B6, B7, C6, C7, D0 } /* column handy ruler: c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 */ #define MATRIX_COL_PINS { D1, D2, D3, D4, D5, D6, D7, F0, F1, F4 } -#define UNUSED_PINS { AF } +#define UNUSED_PINS { } //NOTE: if D6 pin shows any issues in exploatation the LED on the Teensy is to be removed diff --git a/keyboards/esca/getawayvan/config.h b/keyboards/esca/getawayvan/config.h index e9fbc6b404..3b718c5835 100644 --- a/keyboards/esca/getawayvan/config.h +++ b/keyboards/esca/getawayvan/config.h @@ -35,7 +35,6 @@ along with this program. If not, see . #define MATRIX_ROW_PINS { A9, A8, A3, A5 } #define DIODE_DIRECTION COL2ROW -#define BACKLIGHT_PIN #define BACKLIGHT_LEVELS 6 #define GRAVE_ESC_CTRL_OVERRIDE diff --git a/keyboards/esca/getawayvan_f042/config.h b/keyboards/esca/getawayvan_f042/config.h index f83ec74341..8921e9db4d 100644 --- a/keyboards/esca/getawayvan_f042/config.h +++ b/keyboards/esca/getawayvan_f042/config.h @@ -34,7 +34,6 @@ along with this program. If not, see . #define MATRIX_ROW_PINS { A9, A8, A3, A5 } #define DIODE_DIRECTION COL2ROW -#define BACKLIGHT_PIN #define BACKLIGHT_LEVELS 6 #define GRAVE_ESC_CTRL_OVERRIDE diff --git a/keyboards/handwired/tennie/config.h b/keyboards/handwired/tennie/config.h index fee1258dfd..b1bddac45b 100644 --- a/keyboards/handwired/tennie/config.h +++ b/keyboards/handwired/tennie/config.h @@ -42,7 +42,7 @@ along with this program. If not, see . */ #define MATRIX_ROW_PINS { C6, D4, D0} #define MATRIX_COL_PINS { D7, E6, B4, B5 } -#define UNUSED_PINS { B1, B2, B3, B6, F4, F5, F6, F7 D1} +#define UNUSED_PINS { B1, B2, B3, B6, F4, F5, F6, F7, D1} /* COL2ROW, ROW2COL */ #define DIODE_DIRECTION COL2ROW diff --git a/keyboards/mesa/mesa_tkl/config.h b/keyboards/mesa/mesa_tkl/config.h index f4c26e2a0f..23da260bcf 100644 --- a/keyboards/mesa/mesa_tkl/config.h +++ b/keyboards/mesa/mesa_tkl/config.h @@ -42,7 +42,7 @@ along with this program. If not, see . */ #define MATRIX_ROW_PINS { D2, D1, D0, B0, C6, C7 } #define MATRIX_COL_PINS { D3, D5, D4, D6, D7, B4, B5, B6, F7, F6, F5, F4, F1, F0, B1, B2, B3 } -#define UNUSED_PINS { B7 E6 } +#define UNUSED_PINS { B7, E6 } /* COL2ROW, ROW2COL*/ #define DIODE_DIRECTION COL2ROW diff --git a/keyboards/ploopyco/trackball_nano/rev1_001/config.h b/keyboards/ploopyco/trackball_nano/rev1_001/config.h index 63780ef048..6d265d7233 100644 --- a/keyboards/ploopyco/trackball_nano/rev1_001/config.h +++ b/keyboards/ploopyco/trackball_nano/rev1_001/config.h @@ -29,7 +29,7 @@ * ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode) * */ -#define DIRECT_PINS {} +#define DIRECT_PINS {{NO_PIN}} // These pins are not broken out, and cannot be used normally. // They are set as output and pulled high, by default diff --git a/lib/python/qmk/cli/generate/config_h.py b/lib/python/qmk/cli/generate/config_h.py index 54cd5b96a8..c0c148f1c0 100755 --- a/lib/python/qmk/cli/generate/config_h.py +++ b/lib/python/qmk/cli/generate/config_h.py @@ -12,7 +12,7 @@ from qmk.keyboard import keyboard_completer, keyboard_folder from qmk.path import is_keyboard, normpath -def direct_pins(direct_pins): +def direct_pins(direct_pins, postfix): """Return the config.h lines that set the direct pins. """ rows = [] @@ -24,81 +24,60 @@ def direct_pins(direct_pins): col_count = len(direct_pins[0]) row_count = len(direct_pins) - return """ -#ifndef MATRIX_COLS -# define MATRIX_COLS %s -#endif // MATRIX_COLS + return f""" +#ifndef MATRIX_COLS{postfix} +# define MATRIX_COLS{postfix} {col_count} +#endif // MATRIX_COLS{postfix} -#ifndef MATRIX_ROWS -# define MATRIX_ROWS %s -#endif // MATRIX_ROWS +#ifndef MATRIX_ROWS{postfix} +# define MATRIX_ROWS{postfix} {row_count} +#endif // MATRIX_ROWS{postfix} -#ifndef DIRECT_PINS -# define DIRECT_PINS {%s} -#endif // DIRECT_PINS -""" % (col_count, row_count, ','.join(rows)) +#ifndef DIRECT_PINS{postfix} +# define DIRECT_PINS{postfix} {{ {", ".join(rows)} }} +#endif // DIRECT_PINS{postfix} +""" -def pin_array(define, pins): +def pin_array(define, pins, postfix): """Return the config.h lines that set a pin array. """ pin_num = len(pins) pin_array = ', '.join(map(str, [pin or 'NO_PIN' for pin in pins])) return f""" -#ifndef {define}S -# define {define}S {pin_num} -#endif // {define}S +#ifndef {define}S{postfix} +# define {define}S{postfix} {pin_num} +#endif // {define}S{postfix} -#ifndef {define}_PINS -# define {define}_PINS {{ {pin_array} }} -#endif // {define}_PINS +#ifndef {define}_PINS{postfix} +# define {define}_PINS{postfix} {{ {pin_array} }} +#endif // {define}_PINS{postfix} """ -def matrix_pins(matrix_pins): +def matrix_pins(matrix_pins, postfix=''): """Add the matrix config to the config.h. """ pins = [] if 'direct' in matrix_pins: - pins.append(direct_pins(matrix_pins['direct'])) + pins.append(direct_pins(matrix_pins['direct'], postfix)) if 'cols' in matrix_pins: - pins.append(pin_array('MATRIX_COL', matrix_pins['cols'])) + pins.append(pin_array('MATRIX_COL', matrix_pins['cols'], postfix)) if 'rows' in matrix_pins: - pins.append(pin_array('MATRIX_ROW', matrix_pins['rows'])) + pins.append(pin_array('MATRIX_ROW', matrix_pins['rows'], postfix)) return '\n'.join(pins) -@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.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.') -@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True) -@automagic_keyboard -@automagic_keymap -def generate_config_h(cli): - """Generates the info_config.h file. +def generate_config_items(kb_info_json, config_h_lines): + """Iterate through the info_config map to generate basic config values. """ - # Determine our keyboard(s) - if not cli.config.generate_config_h.keyboard: - cli.log.error('Missing parameter: --keyboard') - cli.subcommands['info'].print_help() - return False - - if not is_keyboard(cli.config.generate_config_h.keyboard): - cli.log.error('Invalid keyboard: "%s"', cli.config.generate_config_h.keyboard) - return False - - # Build the info_config.h file. - kb_info_json = dotty(info_json(cli.config.generate_config_h.keyboard)) info_config_map = json_load(Path('data/mappings/info_config.json')) - config_h_lines = ['/* This file was generated by `qmk generate-config-h`. Do not edit or copy.' ' */', '', '#pragma once'] - - # Iterate through the info_config map to generate basic things for config_key, info_dict in info_config_map.items(): info_key = info_dict['info_key'] key_type = info_dict.get('value_type', 'str') @@ -135,9 +114,78 @@ def generate_config_h(cli): config_h_lines.append(f'# define {config_key} {config_value}') config_h_lines.append(f'#endif // {config_key}') + +def generate_split_config(kb_info_json, config_h_lines): + """Generate the config.h lines for split boards.""" + if 'primary' in kb_info_json['split']: + if kb_info_json['split']['primary'] in ('left', 'right'): + config_h_lines.append('') + config_h_lines.append('#ifndef MASTER_LEFT') + config_h_lines.append('# ifndef MASTER_RIGHT') + if kb_info_json['split']['primary'] == 'left': + config_h_lines.append('# define MASTER_LEFT') + elif kb_info_json['split']['primary'] == 'right': + config_h_lines.append('# define MASTER_RIGHT') + config_h_lines.append('# endif // MASTER_RIGHT') + config_h_lines.append('#endif // MASTER_LEFT') + elif kb_info_json['split']['primary'] == 'pin': + config_h_lines.append('') + config_h_lines.append('#ifndef SPLIT_HAND_PIN') + config_h_lines.append('# define SPLIT_HAND_PIN') + config_h_lines.append('#endif // SPLIT_HAND_PIN') + elif kb_info_json['split']['primary'] == 'matrix_grid': + config_h_lines.append('') + config_h_lines.append('#ifndef SPLIT_HAND_MATRIX_GRID') + config_h_lines.append('# define SPLIT_HAND_MATRIX_GRID {%s}' % (','.join(kb_info_json["split"]["matrix_grid"],))) + config_h_lines.append('#endif // SPLIT_HAND_MATRIX_GRID') + elif kb_info_json['split']['primary'] == 'eeprom': + config_h_lines.append('') + config_h_lines.append('#ifndef EE_HANDS') + config_h_lines.append('# define EE_HANDS') + config_h_lines.append('#endif // EE_HANDS') + + if 'protocol' in kb_info_json['split'].get('transport', {}): + if kb_info_json['split']['transport']['protocol'] == 'i2c': + config_h_lines.append('') + config_h_lines.append('#ifndef USE_I2C') + config_h_lines.append('# define USE_I2C') + config_h_lines.append('#endif // USE_I2C') + + if 'right' in kb_info_json['split'].get('matrix_pins', {}): + config_h_lines.append(matrix_pins(kb_info_json['split']['matrix_pins']['right'], '_RIGHT')) + + +@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.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.') +@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True) +@automagic_keyboard +@automagic_keymap +def generate_config_h(cli): + """Generates the info_config.h file. + """ + # Determine our keyboard(s) + if not cli.config.generate_config_h.keyboard: + cli.log.error('Missing parameter: --keyboard') + cli.subcommands['info'].print_help() + return False + + if not is_keyboard(cli.config.generate_config_h.keyboard): + cli.log.error('Invalid keyboard: "%s"', cli.config.generate_config_h.keyboard) + return False + + # Build the info_config.h file. + kb_info_json = dotty(info_json(cli.config.generate_config_h.keyboard)) + config_h_lines = ['/* This file was generated by `qmk generate-config-h`. Do not edit or copy.' ' */', '', '#pragma once'] + + generate_config_items(kb_info_json, config_h_lines) + if 'matrix_pins' in kb_info_json: config_h_lines.append(matrix_pins(kb_info_json['matrix_pins'])) + if 'split' in kb_info_json: + generate_split_config(kb_info_json, config_h_lines) + # Show the results config_h = '\n'.join(config_h_lines) diff --git a/lib/python/qmk/cli/generate/info_json.py b/lib/python/qmk/cli/generate/info_json.py index 8931b68b6f..284d1a8510 100755 --- a/lib/python/qmk/cli/generate/info_json.py +++ b/lib/python/qmk/cli/generate/info_json.py @@ -4,15 +4,17 @@ Compile an info.json for a particular keyboard and pretty-print it. """ import json -from jsonschema import Draft7Validator, validators +from argcomplete.completers import FilesCompleter +from jsonschema import Draft7Validator, RefResolver, validators from milc import cli +from pathlib import Path from qmk.decorators import automagic_keyboard, automagic_keymap from qmk.info import info_json from qmk.json_encoders import InfoJSONEncoder -from qmk.json_schema import load_jsonschema +from qmk.json_schema import compile_schema_store from qmk.keyboard import keyboard_completer, keyboard_folder -from qmk.path import is_keyboard +from qmk.path import is_keyboard, normpath def pruning_validator(validator_class): @@ -34,15 +36,19 @@ def pruning_validator(validator_class): def strip_info_json(kb_info_json): """Remove the API-only properties from the info.json. """ + schema_store = compile_schema_store() pruning_draft_7_validator = pruning_validator(Draft7Validator) - schema = load_jsonschema('keyboard') - validator = pruning_draft_7_validator(schema).validate + schema = schema_store['qmk.keyboard.v1'] + resolver = RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store) + validator = pruning_draft_7_validator(schema, resolver=resolver).validate return validator(kb_info_json) @cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.') @cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.') +@cli.argument('-o', '--output', arg_only=True, completer=FilesCompleter, help='Write the output the specified file, overwriting if necessary.') +@cli.argument('-ow', '--overwrite', arg_only=True, action='store_true', help='Overwrite the existing info.json. (Overrides the location of --output)') @cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True) @automagic_keyboard @automagic_keymap @@ -59,9 +65,29 @@ def generate_info_json(cli): cli.log.error('Invalid keyboard: "%s"', cli.config.generate_info_json.keyboard) return False + if cli.args.overwrite: + output_path = (Path('keyboards') / cli.config.generate_info_json.keyboard / 'info.json').resolve() + + if cli.args.output: + cli.log.warning('Overwriting user supplied --output with %s', output_path) + + cli.args.output = output_path + # Build the info.json file kb_info_json = info_json(cli.config.generate_info_json.keyboard) strip_info_json(kb_info_json) + info_json_text = json.dumps(kb_info_json, indent=4, cls=InfoJSONEncoder) + + if cli.args.output: + # Write to a file + output_path = normpath(cli.args.output) + + if output_path.exists(): + cli.log.warning('Overwriting output file %s', output_path) + + output_path.write_text(info_json_text + '\n') + cli.log.info('Wrote info.json to %s.', output_path) - # Display the results - print(json.dumps(kb_info_json, indent=2, cls=InfoJSONEncoder)) + else: + # Display the results + print(info_json_text) diff --git a/lib/python/qmk/cli/generate/rules_mk.py b/lib/python/qmk/cli/generate/rules_mk.py index 41c94e16b5..2712b81cb5 100755 --- a/lib/python/qmk/cli/generate/rules_mk.py +++ b/lib/python/qmk/cli/generate/rules_mk.py @@ -76,6 +76,17 @@ def generate_rules_mk(cli): enabled = 'yes' if enabled else 'no' rules_mk_lines.append(f'{feature}_ENABLE ?= {enabled}') + # Set SPLIT_TRANSPORT, if needed + if kb_info_json.get('split', {}).get('transport', {}).get('protocol') == 'custom': + rules_mk_lines.append('SPLIT_TRANSPORT ?= custom') + + # Set CUSTOM_MATRIX, if needed + if kb_info_json.get('matrix_pins', {}).get('custom'): + if kb_info_json.get('matrix_pins', {}).get('custom_lite'): + rules_mk_lines.append('CUSTOM_MATRIX ?= lite') + else: + rules_mk_lines.append('CUSTOM_MATRIX ?= yes') + # Show the results rules_mk = '\n'.join(rules_mk_lines) + '\n' diff --git a/lib/python/qmk/cli/info.py b/lib/python/qmk/cli/info.py index 337b494a99..3131d4b53f 100755 --- a/lib/python/qmk/cli/info.py +++ b/lib/python/qmk/cli/info.py @@ -24,19 +24,15 @@ def show_keymap(kb_info_json, title_caps=True): keymap_path = locate_keymap(cli.config.info.keyboard, cli.config.info.keymap) if keymap_path and keymap_path.suffix == '.json': - if title_caps: - cli.echo('{fg_blue}Keymap "%s"{fg_reset}:', cli.config.info.keymap) - else: - cli.echo('{fg_blue}keymap_%s{fg_reset}:', cli.config.info.keymap) - keymap_data = json.load(keymap_path.open(encoding='utf-8')) layout_name = keymap_data['layout'] + layout_name = kb_info_json.get('layout_aliases', {}).get(layout_name, layout_name) # Resolve alias names for layer_num, layer in enumerate(keymap_data['layers']): if title_caps: - cli.echo('{fg_cyan}Layer %s{fg_reset}:', layer_num) + cli.echo('{fg_cyan}Keymap %s Layer %s{fg_reset}:', cli.config.info.keymap, layer_num) else: - cli.echo('{fg_cyan}layer_%s{fg_reset}:', layer_num) + cli.echo('{fg_cyan}keymap.%s.layer.%s{fg_reset}:', cli.config.info.keymap, layer_num) print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, layer)) @@ -45,7 +41,7 @@ def show_layouts(kb_info_json, title_caps=True): """Render the layouts with info.json labels. """ for layout_name, layout_art in render_layouts(kb_info_json, cli.config.info.ascii).items(): - title = layout_name.title() if title_caps else layout_name + title = f'Layout {layout_name.title()}' if title_caps else f'layouts.{layout_name}' cli.echo('{fg_cyan}%s{fg_reset}:', title) print(layout_art) # Avoid passing dirty data to cli.echo() @@ -93,15 +89,6 @@ def print_friendly_output(kb_info_json): aliases = [f'{key}={value}' for key, value in kb_info_json['layout_aliases'].items()] cli.echo('{fg_blue}Layout aliases:{fg_reset} %s' % (', '.join(aliases),)) - if cli.config.info.layouts: - show_layouts(kb_info_json, True) - - if cli.config.info.matrix: - show_matrix(kb_info_json, True) - - if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file': - show_keymap(kb_info_json, True) - def print_text_output(kb_info_json): """Print the info.json in a plain text format. @@ -122,6 +109,24 @@ def print_text_output(kb_info_json): show_keymap(kb_info_json, False) +def print_dotted_output(kb_info_json, prefix=''): + """Print the info.json in a plain text format with dot-joined keys. + """ + for key in sorted(kb_info_json): + new_prefix = f'{prefix}.{key}' if prefix else key + + if key in ['parse_errors', 'parse_warnings']: + continue + elif key == 'layouts' and prefix == '': + cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys()))) + elif isinstance(kb_info_json[key], dict): + print_dotted_output(kb_info_json[key], new_prefix) + elif isinstance(kb_info_json[key], list): + cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, ', '.join(map(str, sorted(kb_info_json[key])))) + else: + cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, kb_info_json[key]) + + def print_parsed_rules_mk(keyboard_name): rules = rules_mk(keyboard_name) for k in sorted(rules.keys()): @@ -162,10 +167,22 @@ def info(cli): # Output in the requested format if cli.args.format == 'json': print(json.dumps(kb_info_json, cls=InfoJSONEncoder)) + return True elif cli.args.format == 'text': - print_text_output(kb_info_json) + print_dotted_output(kb_info_json) + title_caps = False elif cli.args.format == 'friendly': print_friendly_output(kb_info_json) + title_caps = True else: cli.log.error('Unknown format: %s', cli.args.format) return False + + if cli.config.info.layouts: + show_layouts(kb_info_json, title_caps) + + if cli.config.info.matrix: + show_matrix(kb_info_json, title_caps) + + if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file': + show_keymap(kb_info_json, title_caps) diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index 858fbab335..b6f2ecf644 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -61,8 +61,8 @@ def info_json(keyboard): # Merge in the data from info.json, config.h, and rules.mk info_data = merge_info_jsons(keyboard, info_data) - info_data = _extract_config_h(info_data) info_data = _extract_rules_mk(info_data) + info_data = _extract_config_h(info_data) # Ensure that we have matrix row and column counts info_data = _matrix_size(info_data) @@ -161,10 +161,9 @@ def _extract_pins(pins): return [_pin_name(pin) for pin in pins.split(',')] -def _extract_direct_matrix(info_data, direct_pins): +def _extract_direct_matrix(direct_pins): """ """ - info_data['matrix_pins'] = {} direct_pin_array = [] while direct_pins[-1] != '}': @@ -188,12 +187,157 @@ def _extract_direct_matrix(info_data, direct_pins): return direct_pin_array +def _extract_audio(info_data, config_c): + """Populate data about the audio configuration + """ + audio_pins = [] + + for pin in 'B5', 'B6', 'B7', 'C4', 'C5', 'C6': + if config_c.get(f'{pin}_AUDIO'): + audio_pins.append(pin) + + if audio_pins: + info_data['audio'] = {'pins': audio_pins} + + +def _extract_split_main(info_data, config_c): + """Populate data about the split configuration + """ + # Figure out how the main half is determined + if config_c.get('SPLIT_HAND_PIN') is True: + if 'split' not in info_data: + info_data['split'] = {} + + if 'main' in info_data['split']: + _log_warning(info_data, 'Split main hand is specified in both config.h (SPLIT_HAND_PIN) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main']) + + info_data['split']['main'] = 'pin' + + if config_c.get('SPLIT_HAND_MATRIX_GRID'): + if 'split' not in info_data: + info_data['split'] = {} + + if 'main' in info_data['split']: + _log_warning(info_data, 'Split main hand is specified in both config.h (SPLIT_HAND_MATRIX_GRID) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main']) + + info_data['split']['main'] = 'matrix_grid' + info_data['split']['matrix_grid'] = _extract_pins(config_c['SPLIT_HAND_MATRIX_GRID']) + + if config_c.get('EE_HANDS') is True: + if 'split' not in info_data: + info_data['split'] = {} + + if 'main' in info_data['split']: + _log_warning(info_data, 'Split main hand is specified in both config.h (EE_HANDS) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main']) + + info_data['split']['main'] = 'eeprom' + + if config_c.get('MASTER_RIGHT') is True: + if 'split' not in info_data: + info_data['split'] = {} + + if 'main' in info_data['split']: + _log_warning(info_data, 'Split main hand is specified in both config.h (MASTER_RIGHT) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main']) + + info_data['split']['main'] = 'right' + + if config_c.get('MASTER_LEFT') is True: + if 'split' not in info_data: + info_data['split'] = {} + + if 'main' in info_data['split']: + _log_warning(info_data, 'Split main hand is specified in both config.h (MASTER_LEFT) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main']) + + info_data['split']['main'] = 'left' + + +def _extract_split_transport(info_data, config_c): + # Figure out the transport method + if config_c.get('USE_I2C') is True: + if 'split' not in info_data: + info_data['split'] = {} + + if 'transport' not in info_data['split']: + info_data['split']['transport'] = {} + + if 'protocol' in info_data['split']['transport']: + _log_warning(info_data, 'Split transport is specified in both config.h (USE_I2C) and info.json (split.transport.protocol) (Value: %s), the config.h value wins.' % info_data['split']['transport']) + + info_data['split']['transport']['protocol'] = 'i2c' + + elif 'protocol' not in info_data.get('split', {}).get('transport', {}): + if 'split' not in info_data: + info_data['split'] = {} + + if 'transport' not in info_data['split']: + info_data['split']['transport'] = {} + + info_data['split']['transport']['protocol'] = 'serial' + + +def _extract_split_right_pins(info_data, config_c): + # Figure out the right half matrix pins + row_pins = config_c.get('MATRIX_ROW_PINS_RIGHT', '').replace('{', '').replace('}', '').strip() + col_pins = config_c.get('MATRIX_COL_PINS_RIGHT', '').replace('{', '').replace('}', '').strip() + unused_pin_text = config_c.get('UNUSED_PINS_RIGHT') + unused_pins = unused_pin_text.replace('{', '').replace('}', '').strip() if isinstance(unused_pin_text, str) else None + direct_pins = config_c.get('DIRECT_PINS_RIGHT', '').replace(' ', '')[1:-1] + + if row_pins and col_pins: + if info_data.get('split', {}).get('matrix_pins', {}).get('right') in info_data: + _log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.') + + if 'split' not in info_data: + info_data['split'] = {} + + if 'matrix_pins' not in info_data['split']: + info_data['split']['matrix_pins'] = {} + + if 'right' not in info_data['split']['matrix_pins']: + info_data['split']['matrix_pins']['right'] = {} + + info_data['split']['matrix_pins']['right'] = { + 'cols': _extract_pins(col_pins), + 'rows': _extract_pins(row_pins), + } + + if direct_pins: + if info_data.get('split', {}).get('matrix_pins', {}).get('right', {}): + _log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.') + + if 'split' not in info_data: + info_data['split'] = {} + + if 'matrix_pins' not in info_data['split']: + info_data['split']['matrix_pins'] = {} + + if 'right' not in info_data['split']['matrix_pins']: + info_data['split']['matrix_pins']['right'] = {} + + info_data['split']['matrix_pins']['right']['direct'] = _extract_direct_matrix(direct_pins) + + if unused_pins: + if 'split' not in info_data: + info_data['split'] = {} + + if 'matrix_pins' not in info_data['split']: + info_data['split']['matrix_pins'] = {} + + if 'right' not in info_data['split']['matrix_pins']: + info_data['split']['matrix_pins']['right'] = {} + + info_data['split']['matrix_pins']['right']['unused'] = _extract_pins(unused_pins) + + def _extract_matrix_info(info_data, config_c): """Populate the matrix information. """ row_pins = config_c.get('MATRIX_ROW_PINS', '').replace('{', '').replace('}', '').strip() col_pins = config_c.get('MATRIX_COL_PINS', '').replace('{', '').replace('}', '').strip() + unused_pin_text = config_c.get('UNUSED_PINS') + unused_pins = unused_pin_text.replace('{', '').replace('}', '').strip() if isinstance(unused_pin_text, str) else None direct_pins = config_c.get('DIRECT_PINS', '').replace(' ', '')[1:-1] + info_snippet = {} if 'MATRIX_ROWS' in config_c and 'MATRIX_COLS' in config_c: if 'matrix_size' in info_data: @@ -205,19 +349,35 @@ def _extract_matrix_info(info_data, config_c): } if row_pins and col_pins: - if 'matrix_pins' in info_data: + if 'matrix_pins' in info_data and 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']: _log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.') - info_data['matrix_pins'] = { - 'cols': _extract_pins(col_pins), - 'rows': _extract_pins(row_pins), - } + info_snippet['cols'] = _extract_pins(col_pins) + info_snippet['rows'] = _extract_pins(row_pins) if direct_pins: - if 'matrix_pins' in info_data: + if 'matrix_pins' in info_data and 'direct' in info_data['matrix_pins']: _log_warning(info_data, 'Direct pins are specified in both info.json and config.h, the config.h values win.') - info_data['matrix_pins']['direct'] = _extract_direct_matrix(info_data, direct_pins) + info_snippet['direct'] = _extract_direct_matrix(direct_pins) + + if unused_pins: + if 'matrix_pins' not in info_data: + info_data['matrix_pins'] = {} + + info_snippet['unused'] = _extract_pins(unused_pins) + + if config_c.get('CUSTOM_MATRIX', 'no') != 'no': + if 'matrix_pins' in info_data and 'custom' in info_data['matrix_pins']: + _log_warning(info_data, 'Custom Matrix is specified in both info.json and config.h, the config.h values win.') + + info_snippet['custom'] = True + + if config_c['CUSTOM_MATRIX'] == 'lite': + info_snippet['custom_lite'] = True + + if info_snippet: + info_data['matrix_pins'] = info_snippet return info_data @@ -275,6 +435,10 @@ def _extract_config_h(info_data): # Pull data that easily can't be mapped in json _extract_matrix_info(info_data, config_c) + _extract_audio(info_data, config_c) + _extract_split_main(info_data, config_c) + _extract_split_transport(info_data, config_c) + _extract_split_right_pins(info_data, config_c) return info_data diff --git a/lib/python/qmk/json_schema.py b/lib/python/qmk/json_schema.py index cbc5bff518..ffc7c6bcd1 100644 --- a/lib/python/qmk/json_schema.py +++ b/lib/python/qmk/json_schema.py @@ -2,6 +2,7 @@ """ import json from collections.abc import Mapping +from functools import lru_cache from pathlib import Path import hjson @@ -25,6 +26,7 @@ def json_load(json_file): exit(1) +@lru_cache(maxsize=0) def load_jsonschema(schema_name): """Read a jsonschema file from disk. """ @@ -39,8 +41,9 @@ def load_jsonschema(schema_name): return json_load(schema_path) -def create_validator(schema): - """Creates a validator for the given schema id. +@lru_cache(maxsize=0) +def compile_schema_store(): + """Compile all our schemas into a schema store. """ schema_store = {} @@ -51,6 +54,14 @@ def create_validator(schema): continue schema_store[schema_data['$id']] = schema_data + return schema_store + + +@lru_cache(maxsize=0) +def create_validator(schema): + """Creates a validator for the given schema id. + """ + schema_store = compile_schema_store() resolver = jsonschema.RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store) return jsonschema.Draft7Validator(schema_store[schema], resolver=resolver).validate -- cgit v1.2.3 From 2e734fb6b9e3480854b794218d381559aab431b7 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Wed, 18 Aug 2021 21:52:41 +0100 Subject: Add config.h and rules.mk support for data driven keymaps (#12859) * Add config.h and rules.mk support for data driven keymaps * tidy up after rebase * Rename key as it can contain more than just keyboard overrides * tidy up after rebase * Add validation--- build_json.mk | 14 ------- build_keyboard.mk | 46 +++++++++++++++++----- build_layout.mk | 5 --- data/mappings/info_config.json | 2 + data/schemas/keymap.jsonschema | 24 +++++++++++ .../minivan/keymaps/via/keymap.json | 11 +++++- .../thevankeyboards/minivan/keymaps/via/rules.mk | 2 - lib/python/qmk/cli/generate/config_h.py | 29 ++++++-------- lib/python/qmk/cli/generate/keyboard_h.py | 7 +--- lib/python/qmk/cli/generate/rules_mk.py | 30 +++++++------- 10 files changed, 101 insertions(+), 69 deletions(-) create mode 100644 data/schemas/keymap.jsonschema delete mode 100644 keyboards/thevankeyboards/minivan/keymaps/via/rules.mk (limited to 'lib/python/qmk/cli/generate') diff --git a/build_json.mk b/build_json.mk index 28659f2603..0c034eb2ae 100644 --- a/build_json.mk +++ b/build_json.mk @@ -1,31 +1,17 @@ # Look for a json keymap file ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_5)/keymap.json)","") - KEYMAP_C := $(KEYMAP_OUTPUT)/keymap.c KEYMAP_JSON := $(MAIN_KEYMAP_PATH_5)/keymap.json KEYMAP_PATH := $(MAIN_KEYMAP_PATH_5) else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_4)/keymap.json)","") - KEYMAP_C := $(KEYMAP_OUTPUT)/keymap.c KEYMAP_JSON := $(MAIN_KEYMAP_PATH_4)/keymap.json KEYMAP_PATH := $(MAIN_KEYMAP_PATH_4) else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_3)/keymap.json)","") - KEYMAP_C := $(KEYMAP_OUTPUT)/keymap.c KEYMAP_JSON := $(MAIN_KEYMAP_PATH_3)/keymap.json KEYMAP_PATH := $(MAIN_KEYMAP_PATH_3) else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_2)/keymap.json)","") - KEYMAP_C := $(KEYMAP_OUTPUT)/keymap.c KEYMAP_JSON := $(MAIN_KEYMAP_PATH_2)/keymap.json KEYMAP_PATH := $(MAIN_KEYMAP_PATH_2) else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_1)/keymap.json)","") - KEYMAP_C := $(KEYMAP_OUTPUT)/keymap.c KEYMAP_JSON := $(MAIN_KEYMAP_PATH_1)/keymap.json KEYMAP_PATH := $(MAIN_KEYMAP_PATH_1) endif - -# Load the keymap-level rules.mk if exists -ifneq ("$(wildcard $(KEYMAP_PATH))", "") - -include $(KEYMAP_PATH)/rules.mk -endif - -# Generate the keymap.c -$(KEYMAP_C): $(KEYMAP_JSON) - $(QMK_BIN) json2c --quiet --output $(KEYMAP_C) $(KEYMAP_JSON) diff --git a/build_keyboard.mk b/build_keyboard.mk index 46d1e45667..daef76080a 100644 --- a/build_keyboard.mk +++ b/build_keyboard.mk @@ -23,6 +23,15 @@ KEYBOARD_OUTPUT := $(BUILD_DIR)/obj_$(KEYBOARD_FILESAFE) # Force expansion TARGET := $(TARGET) +ifneq ($(FORCE_LAYOUT),) + TARGET := $(TARGET)_$(FORCE_LAYOUT) +endif + +# Object files and generated keymap directory +# To put object files in current directory, use a dot (.), do NOT make +# this an empty or blank macro! +KEYMAP_OUTPUT := $(BUILD_DIR)/obj_$(TARGET) + # For split boards we need to set a master half. MASTER ?= left ifdef master @@ -100,18 +109,9 @@ MAIN_KEYMAP_PATH_4 := $(KEYBOARD_PATH_4)/keymaps/$(KEYMAP) MAIN_KEYMAP_PATH_5 := $(KEYBOARD_PATH_5)/keymaps/$(KEYMAP) # Pull in rules from info.json -INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/rules.mk) +INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/info_rules.mk) include $(INFO_RULES_MK) -ifneq ($(FORCE_LAYOUT),) - TARGET := $(TARGET)_$(FORCE_LAYOUT) -endif - -# Object files and generated keymap directory -# To put object files in current directory, use a dot (.), do NOT make -# this an empty or blank macro! -KEYMAP_OUTPUT := $(BUILD_DIR)/obj_$(TARGET) - # Check for keymap.json first, so we can regenerate keymap.c include build_json.mk @@ -146,6 +146,29 @@ ifeq ("$(wildcard $(KEYMAP_PATH))", "") endif endif +# Have we found a keymap.json? +ifneq ("$(wildcard $(KEYMAP_JSON))", "") + KEYMAP_C := $(KEYMAP_OUTPUT)/keymap.c + KEYMAP_H := $(KEYMAP_OUTPUT)/config.h + + # Load the keymap-level rules.mk if exists + -include $(KEYMAP_PATH)/rules.mk + + # Load any rules.mk content from keymap.json + INFO_RULES_MK = $(shell (QMK_BIN) generate-rules-mk --quiet --escape --keyboard $(KEYBOARD) --keymap $(KEYMAP) --output $(KEYMAP_OUTPUT)/rules.mk) + include $(INFO_RULES_MK) + +# Add rules to enerate the keymap files - indentation here is important +$(KEYMAP_OUTPUT)/keymap.c: $(KEYMAP_JSON) + (QMK_BIN) json2c --quiet --output $(KEYMAP_C) $(KEYMAP_JSON) + +$(KEYMAP_OUTPUT)/config.h: $(KEYMAP_JSON) + (QMK_BIN) generate-config-h --quiet --keyboard $(KEYBOARD) --keymap $(KEYMAP) --output $(KEYMAP_OUTPUT)/config.h + +generated-files: $(KEYMAP_OUTPUT)/config.h $(KEYMAP_OUTPUT)/keymap.c + +endif + ifeq ($(strip $(CTPC)), yes) CONVERT_TO_PROTON_C=yes endif @@ -336,6 +359,9 @@ endif ifneq ("$(wildcard $(KEYMAP_PATH)/config.h)","") CONFIG_H += $(KEYMAP_PATH)/config.h endif +ifneq ("$(KEYMAP_H)","") + CONFIG_H += $(KEYMAP_H) +endif # project specific files SRC += \ diff --git a/build_layout.mk b/build_layout.mk index 649dfb2c70..b4b00793ea 100644 --- a/build_layout.mk +++ b/build_layout.mk @@ -7,7 +7,6 @@ define SEARCH_LAYOUTS_REPO LAYOUT_KEYMAP_C := $$(LAYOUT_KEYMAP_PATH)/keymap.c ifneq ("$$(wildcard $$(LAYOUT_KEYMAP_JSON))","") -include $$(LAYOUT_KEYMAP_PATH)/rules.mk - KEYMAP_C := $(KEYMAP_OUTPUT)/keymap.c KEYMAP_JSON := $$(LAYOUT_KEYMAP_JSON) KEYMAP_PATH := $$(LAYOUT_KEYMAP_PATH) else ifneq ("$$(wildcard $$(LAYOUT_KEYMAP_C))","") @@ -31,7 +30,3 @@ ifneq ($(FORCE_LAYOUT),) endif $(foreach LAYOUT,$(LAYOUTS),$(eval $(call SEARCH_LAYOUTS))) - -# Use rule from build_json.mk, but update prerequisite in case KEYMAP_JSON was updated -$(KEYMAP_C): $(KEYMAP_JSON) - $(QMK_BIN) json2c --quiet --output $(KEYMAP_C) $(KEYMAP_JSON) diff --git a/data/mappings/info_config.json b/data/mappings/info_config.json index 18477f9129..72bb0f4a1c 100644 --- a/data/mappings/info_config.json +++ b/data/mappings/info_config.json @@ -18,6 +18,8 @@ "DESCRIPTION": {"info_key": "keyboard_folder", "to_json": false}, "DIODE_DIRECTION": {"info_key": "diode_direction"}, "FORCE_NKRO": {"info_key": "usb.force_nkro", "value_type": "bool"}, + "DYNAMIC_KEYMAP_EEPROM_MAX_ADDR": {"info_key": "dynamic_keymap.eeprom_max_addr", "value_type": "int"}, + "DYNAMIC_KEYMAP_LAYER_COUNT": {"info_key": "dynamic_keymap.layer_count", "value_type": "int"}, "IGNORE_MOD_TAP_INTERRUPT": {"info_key": "tapping.ignore_mod_tap_interrupt", "value_type": "bool"}, "IGNORE_MOD_TAP_INTERRUPT_PER_KEY": {"info_key": "tapping.ignore_mod_tap_interrupt_per_key", "value_type": "bool"}, "LAYOUTS": {"info_key": "layout_aliases", "value_type": "mapping"}, diff --git a/data/schemas/keymap.jsonschema b/data/schemas/keymap.jsonschema new file mode 100644 index 0000000000..35c5b5c98e --- /dev/null +++ b/data/schemas/keymap.jsonschema @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "qmk.keymap.v1", + "title": "Keymap Information", + "type": "object", + "properties": { + "author": {"$ref": "qmk.definitions.v1#/text_identifier"}, + "keyboard": {"$ref": "qmk.definitions.v1#/text_identifier"}, + "keymap": {"$ref": "qmk.definitions.v1#/text_identifier"}, + "layout": {"$ref": "qmk.definitions.v1#/layout_macro"}, + "layers": { + "type": "array", + "items": { + "type": "array", + "items": {"type": "string"} + } + }, + "config": {"$ref": "qmk.keyboard.v1"}, + "notes": { + "type": "string", + "description": "asdf" + } + } +} \ No newline at end of file diff --git a/keyboards/thevankeyboards/minivan/keymaps/via/keymap.json b/keyboards/thevankeyboards/minivan/keymaps/via/keymap.json index 1e9ef88103..54ceb5c342 100644 --- a/keyboards/thevankeyboards/minivan/keymaps/via/keymap.json +++ b/keyboards/thevankeyboards/minivan/keymaps/via/keymap.json @@ -7,5 +7,14 @@ ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"] - ] + ], + "config": { + "features": { + "via": true, + "lto": true + }, + "dynamic_keymap": { + "layer_count": 4 + } + } } diff --git a/keyboards/thevankeyboards/minivan/keymaps/via/rules.mk b/keyboards/thevankeyboards/minivan/keymaps/via/rules.mk deleted file mode 100644 index 36b7ba9cbc..0000000000 --- a/keyboards/thevankeyboards/minivan/keymaps/via/rules.mk +++ /dev/null @@ -1,2 +0,0 @@ -VIA_ENABLE = yes -LTO_ENABLE = yes diff --git a/lib/python/qmk/cli/generate/config_h.py b/lib/python/qmk/cli/generate/config_h.py index c0c148f1c0..ca7e14fe6b 100755 --- a/lib/python/qmk/cli/generate/config_h.py +++ b/lib/python/qmk/cli/generate/config_h.py @@ -5,11 +5,11 @@ from pathlib import Path from dotty_dict import dotty from milc import cli -from qmk.decorators import automagic_keyboard, automagic_keymap from qmk.info import info_json -from qmk.json_schema import json_load +from qmk.json_schema import json_load, validate from qmk.keyboard import keyboard_completer, keyboard_folder -from qmk.path import is_keyboard, normpath +from qmk.keymap import locate_keymap +from qmk.path import normpath def direct_pins(direct_pins, postfix): @@ -157,25 +157,22 @@ def generate_split_config(kb_info_json, config_h_lines): @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.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.') +@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate config.h for.') +@cli.argument('-km', '--keymap', arg_only=True, help='Keymap to generate config.h for.') @cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True) -@automagic_keyboard -@automagic_keymap def generate_config_h(cli): """Generates the info_config.h file. """ - # Determine our keyboard(s) - if not cli.config.generate_config_h.keyboard: - cli.log.error('Missing parameter: --keyboard') - cli.subcommands['info'].print_help() - return False - - if not is_keyboard(cli.config.generate_config_h.keyboard): - cli.log.error('Invalid keyboard: "%s"', cli.config.generate_config_h.keyboard) - return False + # Determine our keyboard/keymap + if cli.args.keymap: + km = locate_keymap(cli.args.keyboard, cli.args.keymap) + km_json = json_load(km) + validate(km_json, 'qmk.keymap.v1') + kb_info_json = dotty(km_json.get('config', {})) + else: + kb_info_json = dotty(info_json(cli.args.keyboard)) # Build the info_config.h file. - kb_info_json = dotty(info_json(cli.config.generate_config_h.keyboard)) config_h_lines = ['/* This file was generated by `qmk generate-config-h`. Do not edit or copy.' ' */', '', '#pragma once'] generate_config_items(kb_info_json, config_h_lines) diff --git a/lib/python/qmk/cli/generate/keyboard_h.py b/lib/python/qmk/cli/generate/keyboard_h.py index 22500dbc91..c9d7f549b3 100755 --- a/lib/python/qmk/cli/generate/keyboard_h.py +++ b/lib/python/qmk/cli/generate/keyboard_h.py @@ -2,7 +2,6 @@ """ from milc import cli -from qmk.decorators import automagic_keyboard, automagic_keymap from qmk.info import info_json from qmk.keyboard import keyboard_completer, keyboard_folder from qmk.path import normpath @@ -29,14 +28,12 @@ def would_populate_layout_h(keyboard): @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.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate keyboard.h for.') +@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate keyboard.h for.') @cli.subcommand('Used by the make system to generate keyboard.h from info.json', hidden=True) -@automagic_keyboard -@automagic_keymap def generate_keyboard_h(cli): """Generates the keyboard.h file. """ - has_layout_h = would_populate_layout_h(cli.config.generate_keyboard_h.keyboard) + has_layout_h = would_populate_layout_h(cli.args.keyboard) # Build the layouts.h file. keyboard_h_lines = ['/* This file was generated by `qmk generate-keyboard-h`. Do not edit or copy.' ' */', '', '#pragma once', '#include "quantum.h"'] diff --git a/lib/python/qmk/cli/generate/rules_mk.py b/lib/python/qmk/cli/generate/rules_mk.py index 2712b81cb5..cdf17dfbcb 100755 --- a/lib/python/qmk/cli/generate/rules_mk.py +++ b/lib/python/qmk/cli/generate/rules_mk.py @@ -5,11 +5,11 @@ from pathlib import Path from dotty_dict import dotty from milc import cli -from qmk.decorators import automagic_keyboard, automagic_keymap from qmk.info import info_json -from qmk.json_schema import json_load +from qmk.json_schema import json_load, validate from qmk.keyboard import keyboard_completer, keyboard_folder -from qmk.path import is_keyboard, normpath +from qmk.keymap import locate_keymap +from qmk.path import normpath def process_mapping_rule(kb_info_json, rules_key, info_dict): @@ -39,23 +39,21 @@ def process_mapping_rule(kb_info_json, rules_key, info_dict): @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.argument('-e', '--escape', arg_only=True, action='store_true', help="Escape spaces in quiet mode") -@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.') -@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True) -@automagic_keyboard -@automagic_keymap +@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate rules.mk for.') +@cli.argument('-km', '--keymap', arg_only=True, help='Keymap to generate rules.mk for.') +@cli.subcommand('Used by the make system to generate rules.mk from info.json', hidden=True) def generate_rules_mk(cli): """Generates a rules.mk file from info.json. """ - if not cli.config.generate_rules_mk.keyboard: - cli.log.error('Missing parameter: --keyboard') - cli.subcommands['info'].print_help() - return False - - if not is_keyboard(cli.config.generate_rules_mk.keyboard): - cli.log.error('Invalid keyboard: "%s"', cli.config.generate_rules_mk.keyboard) - return False + # Determine our keyboard/keymap + if cli.args.keymap: + km = locate_keymap(cli.args.keyboard, cli.args.keymap) + km_json = json_load(km) + validate(km_json, 'qmk.keymap.v1') + kb_info_json = dotty(km_json.get('config', {})) + else: + kb_info_json = dotty(info_json(cli.args.keyboard)) - kb_info_json = dotty(info_json(cli.config.generate_rules_mk.keyboard)) info_rules_map = json_load(Path('data/mappings/info_rules.json')) rules_mk_lines = ['# This file was generated by `qmk generate-rules-mk`. Do not edit or copy.', ''] -- cgit v1.2.3