diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/python/qmk/cli/c2json.py | 3 | ||||
-rwxr-xr-x | lib/python/qmk/cli/compile.py | 3 | ||||
-rw-r--r-- | lib/python/qmk/cli/flash.py | 3 | ||||
-rwxr-xr-x | lib/python/qmk/cli/generate/api.py | 50 | ||||
-rwxr-xr-x | lib/python/qmk/cli/generate/config_h.py | 8 | ||||
-rwxr-xr-x | lib/python/qmk/cli/generate/info_json.py | 8 | ||||
-rwxr-xr-x | lib/python/qmk/cli/generate/layouts.py | 3 | ||||
-rwxr-xr-x | lib/python/qmk/cli/generate/rules_mk.py | 8 | ||||
-rwxr-xr-x | lib/python/qmk/cli/info.py | 4 | ||||
-rw-r--r-- | lib/python/qmk/cli/list/keymaps.py | 8 | ||||
-rwxr-xr-x | lib/python/qmk/cli/new/keymap.py | 3 | ||||
-rw-r--r-- | lib/python/qmk/commands.py | 10 | ||||
-rw-r--r-- | lib/python/qmk/info.py | 69 | ||||
-rw-r--r-- | lib/python/qmk/json_schema.py | 68 | ||||
-rw-r--r-- | lib/python/qmk/keyboard.py | 24 | ||||
-rw-r--r-- | lib/python/qmk/path.py | 1 | ||||
-rw-r--r-- | lib/python/qmk/tests/test_cli_commands.py | 4 |
17 files changed, 171 insertions, 106 deletions
diff --git a/lib/python/qmk/cli/c2json.py b/lib/python/qmk/cli/c2json.py index b9d55ebdb..a97e21222 100644 --- a/lib/python/qmk/cli/c2json.py +++ b/lib/python/qmk/cli/c2json.py | |||
@@ -7,12 +7,13 @@ from milc import cli | |||
7 | import qmk.keymap | 7 | import qmk.keymap |
8 | import qmk.path | 8 | import qmk.path |
9 | from qmk.info_json_encoder import InfoJSONEncoder | 9 | from qmk.info_json_encoder import InfoJSONEncoder |
10 | from qmk.keyboard import keyboard_folder | ||
10 | 11 | ||
11 | 12 | ||
12 | @cli.argument('--no-cpp', arg_only=True, action='store_false', help='Do not use \'cpp\' on keymap.c') | 13 | @cli.argument('--no-cpp', arg_only=True, action='store_false', help='Do not use \'cpp\' on keymap.c') |
13 | @cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to') | 14 | @cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to') |
14 | @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") | 15 | @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") |
15 | @cli.argument('-kb', '--keyboard', arg_only=True, required=True, help='The keyboard\'s name') | 16 | @cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, required=True, help='The keyboard\'s name') |
16 | @cli.argument('-km', '--keymap', arg_only=True, required=True, help='The keymap\'s name') | 17 | @cli.argument('-km', '--keymap', arg_only=True, required=True, help='The keymap\'s name') |
17 | @cli.argument('filename', arg_only=True, help='keymap.c file') | 18 | @cli.argument('filename', arg_only=True, help='keymap.c file') |
18 | @cli.subcommand('Creates a keymap.json from a keymap.c file.') | 19 | @cli.subcommand('Creates a keymap.json from a keymap.c file.') |
diff --git a/lib/python/qmk/cli/compile.py b/lib/python/qmk/cli/compile.py index db195f78a..5793e9892 100755 --- a/lib/python/qmk/cli/compile.py +++ b/lib/python/qmk/cli/compile.py | |||
@@ -7,10 +7,11 @@ from milc import cli | |||
7 | import qmk.path | 7 | import qmk.path |
8 | from qmk.decorators import automagic_keyboard, automagic_keymap | 8 | from qmk.decorators import automagic_keyboard, automagic_keymap |
9 | from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json | 9 | from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json |
10 | from qmk.keyboard import keyboard_folder | ||
10 | 11 | ||
11 | 12 | ||
12 | @cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), help='The configurator export to compile') | 13 | @cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), help='The configurator export to compile') |
13 | @cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.') | 14 | @cli.argument('-kb', '--keyboard', type=keyboard_folder, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.') |
14 | @cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') | 15 | @cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') |
15 | @cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.") | 16 | @cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.") |
16 | @cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.") | 17 | @cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.") |
diff --git a/lib/python/qmk/cli/flash.py b/lib/python/qmk/cli/flash.py index 173dee3df..c9273c3f9 100644 --- a/lib/python/qmk/cli/flash.py +++ b/lib/python/qmk/cli/flash.py | |||
@@ -9,6 +9,7 @@ from milc import cli | |||
9 | import qmk.path | 9 | import qmk.path |
10 | from qmk.decorators import automagic_keyboard, automagic_keymap | 10 | from qmk.decorators import automagic_keyboard, automagic_keymap |
11 | from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json | 11 | from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json |
12 | from qmk.keyboard import keyboard_folder | ||
12 | 13 | ||
13 | 14 | ||
14 | def print_bootloader_help(): | 15 | def print_bootloader_help(): |
@@ -33,7 +34,7 @@ def print_bootloader_help(): | |||
33 | @cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.') | 34 | @cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.') |
34 | @cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.') | 35 | @cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.') |
35 | @cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') | 36 | @cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') |
36 | @cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') | 37 | @cli.argument('-kb', '--keyboard', type=keyboard_folder, help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') |
37 | @cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.") | 38 | @cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.") |
38 | @cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.") | 39 | @cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.") |
39 | @cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.") | 40 | @cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.") |
diff --git a/lib/python/qmk/cli/generate/api.py b/lib/python/qmk/cli/generate/api.py index 6d111f244..9870f7201 100755 --- a/lib/python/qmk/cli/generate/api.py +++ b/lib/python/qmk/cli/generate/api.py | |||
@@ -9,6 +9,7 @@ from milc import cli | |||
9 | from qmk.datetime import current_datetime | 9 | from qmk.datetime import current_datetime |
10 | from qmk.info import info_json | 10 | from qmk.info import info_json |
11 | from qmk.info_json_encoder import InfoJSONEncoder | 11 | from qmk.info_json_encoder import InfoJSONEncoder |
12 | from qmk.json_schema import json_load | ||
12 | from qmk.keyboard import list_keyboards | 13 | from qmk.keyboard import list_keyboards |
13 | 14 | ||
14 | 15 | ||
@@ -18,43 +19,58 @@ def generate_api(cli): | |||
18 | """ | 19 | """ |
19 | api_data_dir = Path('api_data') | 20 | api_data_dir = Path('api_data') |
20 | v1_dir = api_data_dir / 'v1' | 21 | v1_dir = api_data_dir / 'v1' |
21 | keyboard_list = v1_dir / 'keyboard_list.json' | 22 | keyboard_all_file = v1_dir / 'keyboards.json' # A massive JSON containing everything |
22 | keyboard_all = v1_dir / 'keyboards.json' | 23 | keyboard_list_file = v1_dir / 'keyboard_list.json' # A simple list of keyboard targets |
23 | usb_file = v1_dir / 'usb.json' | 24 | keyboard_aliases_file = v1_dir / 'keyboard_aliases.json' # A list of historical keyboard names and their new name |
25 | keyboard_metadata_file = v1_dir / 'keyboard_metadata.json' # All the data configurator/via needs for initialization | ||
26 | usb_file = v1_dir / 'usb.json' # A mapping of USB VID/PID -> keyboard target | ||
24 | 27 | ||
25 | if not api_data_dir.exists(): | 28 | if not api_data_dir.exists(): |
26 | api_data_dir.mkdir() | 29 | api_data_dir.mkdir() |
27 | 30 | ||
28 | kb_all = {'last_updated': current_datetime(), 'keyboards': {}} | 31 | kb_all = {} |
29 | usb_list = {'last_updated': current_datetime(), 'devices': {}} | 32 | usb_list = {} |
30 | 33 | ||
31 | # Generate and write keyboard specific JSON files | 34 | # Generate and write keyboard specific JSON files |
32 | for keyboard_name in list_keyboards(): | 35 | for keyboard_name in list_keyboards(): |
33 | kb_all['keyboards'][keyboard_name] = info_json(keyboard_name) | 36 | kb_all[keyboard_name] = info_json(keyboard_name) |
34 | keyboard_dir = v1_dir / 'keyboards' / keyboard_name | 37 | keyboard_dir = v1_dir / 'keyboards' / keyboard_name |
35 | keyboard_info = keyboard_dir / 'info.json' | 38 | keyboard_info = keyboard_dir / 'info.json' |
36 | keyboard_readme = keyboard_dir / 'readme.md' | 39 | keyboard_readme = keyboard_dir / 'readme.md' |
37 | keyboard_readme_src = Path('keyboards') / keyboard_name / 'readme.md' | 40 | keyboard_readme_src = Path('keyboards') / keyboard_name / 'readme.md' |
38 | 41 | ||
39 | keyboard_dir.mkdir(parents=True, exist_ok=True) | 42 | keyboard_dir.mkdir(parents=True, exist_ok=True) |
40 | keyboard_info.write_text(json.dumps({'last_updated': current_datetime(), 'keyboards': {keyboard_name: kb_all['keyboards'][keyboard_name]}})) | 43 | keyboard_info.write_text(json.dumps({'last_updated': current_datetime(), 'keyboards': {keyboard_name: kb_all[keyboard_name]}})) |
41 | 44 | ||
42 | if keyboard_readme_src.exists(): | 45 | if keyboard_readme_src.exists(): |
43 | copyfile(keyboard_readme_src, keyboard_readme) | 46 | copyfile(keyboard_readme_src, keyboard_readme) |
44 | 47 | ||
45 | if 'usb' in kb_all['keyboards'][keyboard_name]: | 48 | if 'usb' in kb_all[keyboard_name]: |
46 | usb = kb_all['keyboards'][keyboard_name]['usb'] | 49 | usb = kb_all[keyboard_name]['usb'] |
47 | 50 | ||
48 | if 'vid' in usb and usb['vid'] not in usb_list['devices']: | 51 | if 'vid' in usb and usb['vid'] not in usb_list: |
49 | usb_list['devices'][usb['vid']] = {} | 52 | usb_list[usb['vid']] = {} |
50 | 53 | ||
51 | if 'pid' in usb and usb['pid'] not in usb_list['devices'][usb['vid']]: | 54 | if 'pid' in usb and usb['pid'] not in usb_list[usb['vid']]: |
52 | usb_list['devices'][usb['vid']][usb['pid']] = {} | 55 | usb_list[usb['vid']][usb['pid']] = {} |
53 | 56 | ||
54 | if 'vid' in usb and 'pid' in usb: | 57 | if 'vid' in usb and 'pid' in usb: |
55 | usb_list['devices'][usb['vid']][usb['pid']][keyboard_name] = usb | 58 | usb_list[usb['vid']][usb['pid']][keyboard_name] = usb |
56 | 59 | ||
57 | # Write the global JSON files | 60 | # Write the global JSON files |
58 | keyboard_list.write_text(json.dumps({'last_updated': current_datetime(), 'keyboards': sorted(kb_all['keyboards'])}, cls=InfoJSONEncoder)) | 61 | keyboard_all_file.write_text(json.dumps({'last_updated': current_datetime(), 'keyboards': kb_all}, cls=InfoJSONEncoder)) |
59 | keyboard_all.write_text(json.dumps(kb_all, cls=InfoJSONEncoder)) | 62 | usb_file.write_text(json.dumps({'last_updated': current_datetime(), 'usb': usb_list}, cls=InfoJSONEncoder)) |
60 | usb_file.write_text(json.dumps(usb_list, cls=InfoJSONEncoder)) | 63 | |
64 | keyboard_list = sorted(kb_all) | ||
65 | keyboard_list_file.write_text(json.dumps({'last_updated': current_datetime(), 'keyboards': keyboard_list}, cls=InfoJSONEncoder)) | ||
66 | |||
67 | keyboard_aliases = json_load(Path('data/mappings/keyboard_aliases.json')) | ||
68 | keyboard_aliases_file.write_text(json.dumps({'last_updated': current_datetime(), 'keyboard_aliases': keyboard_aliases}, cls=InfoJSONEncoder)) | ||
69 | |||
70 | keyboard_metadata = { | ||
71 | 'last_updated': current_datetime(), | ||
72 | 'keyboards': keyboard_list, | ||
73 | 'keyboard_aliases': keyboard_aliases, | ||
74 | 'usb': usb_list | ||
75 | } | ||
76 | keyboard_metadata_file.write_text(json.dumps(keyboard_metadata, cls=InfoJSONEncoder)) | ||
diff --git a/lib/python/qmk/cli/generate/config_h.py b/lib/python/qmk/cli/generate/config_h.py index e6d49ea4d..ccea6d7a0 100755 --- a/lib/python/qmk/cli/generate/config_h.py +++ b/lib/python/qmk/cli/generate/config_h.py | |||
@@ -6,7 +6,9 @@ from dotty_dict import dotty | |||
6 | from milc import cli | 6 | from milc import cli |
7 | 7 | ||
8 | from qmk.decorators import automagic_keyboard, automagic_keymap | 8 | from qmk.decorators import automagic_keyboard, automagic_keymap |
9 | from qmk.info import _json_load, info_json | 9 | from qmk.info import info_json |
10 | from qmk.json_schema import json_load | ||
11 | from qmk.keyboard import keyboard_folder | ||
10 | from qmk.path import is_keyboard, normpath | 12 | from qmk.path import is_keyboard, normpath |
11 | 13 | ||
12 | 14 | ||
@@ -73,7 +75,7 @@ def matrix_pins(matrix_pins): | |||
73 | 75 | ||
74 | @cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') | 76 | @cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') |
75 | @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") | 77 | @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") |
76 | @cli.argument('-kb', '--keyboard', help='Keyboard to generate config.h for.') | 78 | @cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to generate config.h for.') |
77 | @cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True) | 79 | @cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True) |
78 | @automagic_keyboard | 80 | @automagic_keyboard |
79 | @automagic_keymap | 81 | @automagic_keymap |
@@ -92,7 +94,7 @@ def generate_config_h(cli): | |||
92 | 94 | ||
93 | # Build the info_config.h file. | 95 | # Build the info_config.h file. |
94 | kb_info_json = dotty(info_json(cli.config.generate_config_h.keyboard)) | 96 | kb_info_json = dotty(info_json(cli.config.generate_config_h.keyboard)) |
95 | info_config_map = _json_load(Path('data/mappings/info_config.json')) | 97 | info_config_map = json_load(Path('data/mappings/info_config.json')) |
96 | 98 | ||
97 | config_h_lines = ['/* This file was generated by `qmk generate-config-h`. Do not edit or copy.' ' */', '', '#pragma once'] | 99 | config_h_lines = ['/* This file was generated by `qmk generate-config-h`. Do not edit or copy.' ' */', '', '#pragma once'] |
98 | 100 | ||
diff --git a/lib/python/qmk/cli/generate/info_json.py b/lib/python/qmk/cli/generate/info_json.py index f3fc54ddc..6c00ba7d8 100755 --- a/lib/python/qmk/cli/generate/info_json.py +++ b/lib/python/qmk/cli/generate/info_json.py | |||
@@ -8,8 +8,10 @@ from jsonschema import Draft7Validator, validators | |||
8 | from milc import cli | 8 | from milc import cli |
9 | 9 | ||
10 | from qmk.decorators import automagic_keyboard, automagic_keymap | 10 | from qmk.decorators import automagic_keyboard, automagic_keymap |
11 | from qmk.info import info_json, _jsonschema | 11 | from qmk.info import info_json |
12 | from qmk.info_json_encoder import InfoJSONEncoder | 12 | from qmk.info_json_encoder import InfoJSONEncoder |
13 | from qmk.json_schema import load_jsonschema | ||
14 | from qmk.keyboard import keyboard_folder | ||
13 | from qmk.path import is_keyboard | 15 | from qmk.path import is_keyboard |
14 | 16 | ||
15 | 17 | ||
@@ -33,13 +35,13 @@ def strip_info_json(kb_info_json): | |||
33 | """Remove the API-only properties from the info.json. | 35 | """Remove the API-only properties from the info.json. |
34 | """ | 36 | """ |
35 | pruning_draft_7_validator = pruning_validator(Draft7Validator) | 37 | pruning_draft_7_validator = pruning_validator(Draft7Validator) |
36 | schema = _jsonschema('keyboard') | 38 | schema = load_jsonschema('keyboard') |
37 | validator = pruning_draft_7_validator(schema).validate | 39 | validator = pruning_draft_7_validator(schema).validate |
38 | 40 | ||
39 | return validator(kb_info_json) | 41 | return validator(kb_info_json) |
40 | 42 | ||
41 | 43 | ||
42 | @cli.argument('-kb', '--keyboard', help='Keyboard to show info for.') | 44 | @cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to show info for.') |
43 | @cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.') | 45 | @cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.') |
44 | @cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True) | 46 | @cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True) |
45 | @automagic_keyboard | 47 | @automagic_keyboard |
diff --git a/lib/python/qmk/cli/generate/layouts.py b/lib/python/qmk/cli/generate/layouts.py index a738edfe6..7b4394291 100755 --- a/lib/python/qmk/cli/generate/layouts.py +++ b/lib/python/qmk/cli/generate/layouts.py | |||
@@ -5,6 +5,7 @@ from milc import cli | |||
5 | from qmk.constants import COL_LETTERS, ROW_LETTERS | 5 | from qmk.constants import COL_LETTERS, ROW_LETTERS |
6 | from qmk.decorators import automagic_keyboard, automagic_keymap | 6 | from qmk.decorators import automagic_keyboard, automagic_keymap |
7 | from qmk.info import info_json | 7 | from qmk.info import info_json |
8 | from qmk.keyboard import keyboard_folder | ||
8 | from qmk.path import is_keyboard, normpath | 9 | from qmk.path import is_keyboard, normpath |
9 | 10 | ||
10 | usb_properties = { | 11 | usb_properties = { |
@@ -16,7 +17,7 @@ usb_properties = { | |||
16 | 17 | ||
17 | @cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') | 18 | @cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') |
18 | @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") | 19 | @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") |
19 | @cli.argument('-kb', '--keyboard', help='Keyboard to generate config.h for.') | 20 | @cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to generate config.h for.') |
20 | @cli.subcommand('Used by the make system to generate layouts.h from info.json', hidden=True) | 21 | @cli.subcommand('Used by the make system to generate layouts.h from info.json', hidden=True) |
21 | @automagic_keyboard | 22 | @automagic_keyboard |
22 | @automagic_keymap | 23 | @automagic_keymap |
diff --git a/lib/python/qmk/cli/generate/rules_mk.py b/lib/python/qmk/cli/generate/rules_mk.py index 15917987b..91759d26c 100755 --- a/lib/python/qmk/cli/generate/rules_mk.py +++ b/lib/python/qmk/cli/generate/rules_mk.py | |||
@@ -6,7 +6,9 @@ from dotty_dict import dotty | |||
6 | from milc import cli | 6 | from milc import cli |
7 | 7 | ||
8 | from qmk.decorators import automagic_keyboard, automagic_keymap | 8 | from qmk.decorators import automagic_keyboard, automagic_keymap |
9 | from qmk.info import _json_load, info_json | 9 | from qmk.info import info_json |
10 | from qmk.json_schema import json_load | ||
11 | from qmk.keyboard import keyboard_folder | ||
10 | from qmk.path import is_keyboard, normpath | 12 | from qmk.path import is_keyboard, normpath |
11 | 13 | ||
12 | 14 | ||
@@ -37,7 +39,7 @@ def process_mapping_rule(kb_info_json, rules_key, info_dict): | |||
37 | @cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') | 39 | @cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') |
38 | @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") | 40 | @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") |
39 | @cli.argument('-e', '--escape', arg_only=True, action='store_true', help="Escape spaces in quiet mode") | 41 | @cli.argument('-e', '--escape', arg_only=True, action='store_true', help="Escape spaces in quiet mode") |
40 | @cli.argument('-kb', '--keyboard', help='Keyboard to generate config.h for.') | 42 | @cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to generate config.h for.') |
41 | @cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True) | 43 | @cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True) |
42 | @automagic_keyboard | 44 | @automagic_keyboard |
43 | @automagic_keymap | 45 | @automagic_keymap |
@@ -54,7 +56,7 @@ def generate_rules_mk(cli): | |||
54 | return False | 56 | return False |
55 | 57 | ||
56 | kb_info_json = dotty(info_json(cli.config.generate_rules_mk.keyboard)) | 58 | kb_info_json = dotty(info_json(cli.config.generate_rules_mk.keyboard)) |
57 | info_rules_map = _json_load(Path('data/mappings/info_rules.json')) | 59 | info_rules_map = json_load(Path('data/mappings/info_rules.json')) |
58 | rules_mk_lines = ['# This file was generated by `qmk generate-rules-mk`. Do not edit or copy.', ''] | 60 | rules_mk_lines = ['# This file was generated by `qmk generate-rules-mk`. Do not edit or copy.', ''] |
59 | 61 | ||
60 | # Iterate through the info_rules map to generate basic rules | 62 | # Iterate through the info_rules map to generate basic rules |
diff --git a/lib/python/qmk/cli/info.py b/lib/python/qmk/cli/info.py index a7ce8abf0..88b65686f 100755 --- a/lib/python/qmk/cli/info.py +++ b/lib/python/qmk/cli/info.py | |||
@@ -10,7 +10,7 @@ from milc import cli | |||
10 | from qmk.info_json_encoder import InfoJSONEncoder | 10 | from qmk.info_json_encoder import InfoJSONEncoder |
11 | from qmk.constants import COL_LETTERS, ROW_LETTERS | 11 | from qmk.constants import COL_LETTERS, ROW_LETTERS |
12 | from qmk.decorators import automagic_keyboard, automagic_keymap | 12 | from qmk.decorators import automagic_keyboard, automagic_keymap |
13 | from qmk.keyboard import render_layouts, render_layout | 13 | from qmk.keyboard import keyboard_folder, render_layouts, render_layout |
14 | from qmk.keymap import locate_keymap | 14 | from qmk.keymap import locate_keymap |
15 | from qmk.info import info_json | 15 | from qmk.info import info_json |
16 | from qmk.path import is_keyboard | 16 | from qmk.path import is_keyboard |
@@ -124,7 +124,7 @@ def print_text_output(kb_info_json): | |||
124 | show_keymap(kb_info_json, False) | 124 | show_keymap(kb_info_json, False) |
125 | 125 | ||
126 | 126 | ||
127 | @cli.argument('-kb', '--keyboard', help='Keyboard to show info for.') | 127 | @cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to show info for.') |
128 | @cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.') | 128 | @cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.') |
129 | @cli.argument('-l', '--layouts', action='store_true', help='Render the layouts.') | 129 | @cli.argument('-l', '--layouts', action='store_true', help='Render the layouts.') |
130 | @cli.argument('-m', '--matrix', action='store_true', help='Render the layouts with matrix information.') | 130 | @cli.argument('-m', '--matrix', action='store_true', help='Render the layouts with matrix information.') |
diff --git a/lib/python/qmk/cli/list/keymaps.py b/lib/python/qmk/cli/list/keymaps.py index 49bc84b2c..7c0ad4399 100644 --- a/lib/python/qmk/cli/list/keymaps.py +++ b/lib/python/qmk/cli/list/keymaps.py | |||
@@ -4,18 +4,14 @@ from milc import cli | |||
4 | 4 | ||
5 | import qmk.keymap | 5 | import qmk.keymap |
6 | from qmk.decorators import automagic_keyboard | 6 | from qmk.decorators import automagic_keyboard |
7 | from qmk.path import is_keyboard | 7 | from qmk.keyboard import keyboard_folder |
8 | 8 | ||
9 | 9 | ||
10 | @cli.argument("-kb", "--keyboard", help="Specify keyboard name. Example: 1upkeyboards/1up60hse") | 10 | @cli.argument("-kb", "--keyboard", type=keyboard_folder, help="Specify keyboard name. Example: 1upkeyboards/1up60hse") |
11 | @cli.subcommand("List the keymaps for a specific keyboard") | 11 | @cli.subcommand("List the keymaps for a specific keyboard") |
12 | @automagic_keyboard | 12 | @automagic_keyboard |
13 | def list_keymaps(cli): | 13 | def list_keymaps(cli): |
14 | """List the keymaps for a specific keyboard | 14 | """List the keymaps for a specific keyboard |
15 | """ | 15 | """ |
16 | if not is_keyboard(cli.config.list_keymaps.keyboard): | ||
17 | cli.log.error('Keyboard %s does not exist!', cli.config.list_keymaps.keyboard) | ||
18 | return False | ||
19 | |||
20 | for name in qmk.keymap.list_keymaps(cli.config.list_keymaps.keyboard): | 16 | for name in qmk.keymap.list_keymaps(cli.config.list_keymaps.keyboard): |
21 | print(name) | 17 | print(name) |
diff --git a/lib/python/qmk/cli/new/keymap.py b/lib/python/qmk/cli/new/keymap.py index 52c564997..ea98a287c 100755 --- a/lib/python/qmk/cli/new/keymap.py +++ b/lib/python/qmk/cli/new/keymap.py | |||
@@ -5,10 +5,11 @@ from pathlib import Path | |||
5 | 5 | ||
6 | import qmk.path | 6 | import qmk.path |
7 | from qmk.decorators import automagic_keyboard, automagic_keymap | 7 | from qmk.decorators import automagic_keyboard, automagic_keymap |
8 | from qmk.keyboard import keyboard_folder | ||
8 | from milc import cli | 9 | from milc import cli |
9 | 10 | ||
10 | 11 | ||
11 | @cli.argument('-kb', '--keyboard', help='Specify keyboard name. Example: 1upkeyboards/1up60hse') | 12 | @cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Specify keyboard name. Example: 1upkeyboards/1up60hse') |
12 | @cli.argument('-km', '--keymap', help='Specify the name for the new keymap directory') | 13 | @cli.argument('-km', '--keymap', help='Specify the name for the new keymap directory') |
13 | @cli.subcommand('Creates a new keymap for the keyboard of your choosing') | 14 | @cli.subcommand('Creates a new keymap for the keyboard of your choosing') |
14 | @automagic_keyboard | 15 | @automagic_keyboard |
diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py index 4809365af..510abffdc 100644 --- a/lib/python/qmk/commands.py +++ b/lib/python/qmk/commands.py | |||
@@ -13,6 +13,7 @@ from milc import cli | |||
13 | 13 | ||
14 | import qmk.keymap | 14 | import qmk.keymap |
15 | from qmk.constants import KEYBOARD_OUTPUT_PREFIX | 15 | from qmk.constants import KEYBOARD_OUTPUT_PREFIX |
16 | from qmk.json_schema import json_load | ||
16 | 17 | ||
17 | time_fmt = '%Y-%m-%d-%H:%M:%S' | 18 | time_fmt = '%Y-%m-%d-%H:%M:%S' |
18 | 19 | ||
@@ -191,6 +192,15 @@ def parse_configurator_json(configurator_file): | |||
191 | """ | 192 | """ |
192 | # FIXME(skullydazed/anyone): Add validation here | 193 | # FIXME(skullydazed/anyone): Add validation here |
193 | user_keymap = json.load(configurator_file) | 194 | user_keymap = json.load(configurator_file) |
195 | orig_keyboard = user_keymap['keyboard'] | ||
196 | aliases = json_load(Path('data/mappings/keyboard_aliases.json')) | ||
197 | |||
198 | if orig_keyboard in aliases: | ||
199 | if 'target' in aliases[orig_keyboard]: | ||
200 | user_keymap['keyboard'] = aliases[orig_keyboard]['target'] | ||
201 | |||
202 | if 'layouts' in aliases[orig_keyboard] and user_keymap['layout'] in aliases[orig_keyboard]['layouts']: | ||
203 | user_keymap['layout'] = aliases[orig_keyboard]['layouts'][user_keymap['layout']] | ||
194 | 204 | ||
195 | return user_keymap | 205 | return user_keymap |
196 | 206 | ||
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index 60d3a0132..e2350b7f7 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py | |||
@@ -1,17 +1,15 @@ | |||
1 | """Functions that help us generate and use info.json files. | 1 | """Functions that help us generate and use info.json files. |
2 | """ | 2 | """ |
3 | import json | ||
4 | from collections.abc import Mapping | ||
5 | from glob import glob | 3 | from glob import glob |
6 | from pathlib import Path | 4 | from pathlib import Path |
7 | 5 | ||
8 | import hjson | ||
9 | import jsonschema | 6 | import jsonschema |
10 | from dotty_dict import dotty | 7 | from dotty_dict import dotty |
11 | from milc import cli | 8 | from milc import cli |
12 | 9 | ||
13 | from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS | 10 | from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS |
14 | from qmk.c_parse import find_layouts | 11 | from qmk.c_parse import find_layouts |
12 | from qmk.json_schema import deep_update, json_load, keyboard_validate, keyboard_api_validate | ||
15 | from qmk.keyboard import config_h, rules_mk | 13 | from qmk.keyboard import config_h, rules_mk |
16 | from qmk.keymap import list_keymaps | 14 | from qmk.keymap import list_keymaps |
17 | from qmk.makefile import parse_rules_mk_file | 15 | from qmk.makefile import parse_rules_mk_file |
@@ -82,52 +80,6 @@ def info_json(keyboard): | |||
82 | return info_data | 80 | return info_data |
83 | 81 | ||
84 | 82 | ||
85 | def _json_load(json_file): | ||
86 | """Load a json file from disk. | ||
87 | |||
88 | Note: file must be a Path object. | ||
89 | """ | ||
90 | try: | ||
91 | return hjson.load(json_file.open(encoding='utf-8')) | ||
92 | |||
93 | except json.decoder.JSONDecodeError as e: | ||
94 | cli.log.error('Invalid JSON encountered attempting to load {fg_cyan}%s{fg_reset}:\n\t{fg_red}%s', json_file, e) | ||
95 | exit(1) | ||
96 | |||
97 | |||
98 | def _jsonschema(schema_name): | ||
99 | """Read a jsonschema file from disk. | ||
100 | |||
101 | FIXME(skullydazed/anyone): Refactor to make this a public function. | ||
102 | """ | ||
103 | schema_path = Path(f'data/schemas/{schema_name}.jsonschema') | ||
104 | |||
105 | if not schema_path.exists(): | ||
106 | schema_path = Path('data/schemas/false.jsonschema') | ||
107 | |||
108 | return _json_load(schema_path) | ||
109 | |||
110 | |||
111 | def keyboard_validate(data): | ||
112 | """Validates data against the keyboard jsonschema. | ||
113 | """ | ||
114 | schema = _jsonschema('keyboard') | ||
115 | validator = jsonschema.Draft7Validator(schema).validate | ||
116 | |||
117 | return validator(data) | ||
118 | |||
119 | |||
120 | def keyboard_api_validate(data): | ||
121 | """Validates data against the api_keyboard jsonschema. | ||
122 | """ | ||
123 | base = _jsonschema('keyboard') | ||
124 | relative = _jsonschema('api_keyboard') | ||
125 | resolver = jsonschema.RefResolver.from_schema(base) | ||
126 | validator = jsonschema.Draft7Validator(relative, resolver=resolver).validate | ||
127 | |||
128 | return validator(data) | ||
129 | |||
130 | |||
131 | def _extract_features(info_data, rules): | 83 | def _extract_features(info_data, rules): |
132 | """Find all the features enabled in rules.mk. | 84 | """Find all the features enabled in rules.mk. |
133 | """ | 85 | """ |
@@ -258,7 +210,7 @@ def _extract_config_h(info_data): | |||
258 | 210 | ||
259 | # Pull in data from the json map | 211 | # Pull in data from the json map |
260 | dotty_info = dotty(info_data) | 212 | dotty_info = dotty(info_data) |
261 | info_config_map = _json_load(Path('data/mappings/info_config.json')) | 213 | info_config_map = json_load(Path('data/mappings/info_config.json')) |
262 | 214 | ||
263 | for config_key, info_dict in info_config_map.items(): | 215 | for config_key, info_dict in info_config_map.items(): |
264 | info_key = info_dict['info_key'] | 216 | info_key = info_dict['info_key'] |
@@ -326,7 +278,7 @@ def _extract_rules_mk(info_data): | |||
326 | 278 | ||
327 | # Pull in data from the json map | 279 | # Pull in data from the json map |
328 | dotty_info = dotty(info_data) | 280 | dotty_info = dotty(info_data) |
329 | info_rules_map = _json_load(Path('data/mappings/info_rules.json')) | 281 | info_rules_map = json_load(Path('data/mappings/info_rules.json')) |
330 | 282 | ||
331 | for rules_key, info_dict in info_rules_map.items(): | 283 | for rules_key, info_dict in info_rules_map.items(): |
332 | info_key = info_dict['info_key'] | 284 | info_key = info_dict['info_key'] |
@@ -516,25 +468,12 @@ def unknown_processor_rules(info_data, rules): | |||
516 | return info_data | 468 | return info_data |
517 | 469 | ||
518 | 470 | ||
519 | def deep_update(origdict, newdict): | ||
520 | """Update a dictionary in place, recursing to do a deep copy. | ||
521 | """ | ||
522 | for key, value in newdict.items(): | ||
523 | if isinstance(value, Mapping): | ||
524 | origdict[key] = deep_update(origdict.get(key, {}), value) | ||
525 | |||
526 | else: | ||
527 | origdict[key] = value | ||
528 | |||
529 | return origdict | ||
530 | |||
531 | |||
532 | def merge_info_jsons(keyboard, info_data): | 471 | def merge_info_jsons(keyboard, info_data): |
533 | """Return a merged copy of all the info.json files for a keyboard. | 472 | """Return a merged copy of all the info.json files for a keyboard. |
534 | """ | 473 | """ |
535 | for info_file in find_info_json(keyboard): | 474 | for info_file in find_info_json(keyboard): |
536 | # Load and validate the JSON data | 475 | # Load and validate the JSON data |
537 | new_info_data = _json_load(info_file) | 476 | new_info_data = json_load(info_file) |
538 | 477 | ||
539 | if not isinstance(new_info_data, dict): | 478 | if not isinstance(new_info_data, dict): |
540 | _log_error(info_data, "Invalid file %s, root object should be a dictionary." % (str(info_file),)) | 479 | _log_error(info_data, "Invalid file %s, root object should be a dictionary." % (str(info_file),)) |
diff --git a/lib/python/qmk/json_schema.py b/lib/python/qmk/json_schema.py new file mode 100644 index 000000000..b4cd1776b --- /dev/null +++ b/lib/python/qmk/json_schema.py | |||
@@ -0,0 +1,68 @@ | |||
1 | """Functions that help us generate and use info.json files. | ||
2 | """ | ||
3 | import json | ||
4 | from collections.abc import Mapping | ||
5 | from pathlib import Path | ||
6 | |||
7 | import hjson | ||
8 | import jsonschema | ||
9 | from milc import cli | ||
10 | |||
11 | |||
12 | def json_load(json_file): | ||
13 | """Load a json file from disk. | ||
14 | |||
15 | Note: file must be a Path object. | ||
16 | """ | ||
17 | try: | ||
18 | return hjson.load(json_file.open()) | ||
19 | |||
20 | except json.decoder.JSONDecodeError as e: | ||
21 | cli.log.error('Invalid JSON encountered attempting to load {fg_cyan}%s{fg_reset}:\n\t{fg_red}%s', json_file, e) | ||
22 | exit(1) | ||
23 | |||
24 | |||
25 | def load_jsonschema(schema_name): | ||
26 | """Read a jsonschema file from disk. | ||
27 | |||
28 | FIXME(skullydazed/anyone): Refactor to make this a public function. | ||
29 | """ | ||
30 | schema_path = Path(f'data/schemas/{schema_name}.jsonschema') | ||
31 | |||
32 | if not schema_path.exists(): | ||
33 | schema_path = Path('data/schemas/false.jsonschema') | ||
34 | |||
35 | return json_load(schema_path) | ||
36 | |||
37 | |||
38 | def keyboard_validate(data): | ||
39 | """Validates data against the keyboard jsonschema. | ||
40 | """ | ||
41 | schema = load_jsonschema('keyboard') | ||
42 | validator = jsonschema.Draft7Validator(schema).validate | ||
43 | |||
44 | return validator(data) | ||
45 | |||
46 | |||
47 | def keyboard_api_validate(data): | ||
48 | """Validates data against the api_keyboard jsonschema. | ||
49 | """ | ||
50 | base = load_jsonschema('keyboard') | ||
51 | relative = load_jsonschema('api_keyboard') | ||
52 | resolver = jsonschema.RefResolver.from_schema(base) | ||
53 | validator = jsonschema.Draft7Validator(relative, resolver=resolver).validate | ||
54 | |||
55 | return validator(data) | ||
56 | |||
57 | |||
58 | def deep_update(origdict, newdict): | ||
59 | """Update a dictionary in place, recursing to do a deep copy. | ||
60 | """ | ||
61 | for key, value in newdict.items(): | ||
62 | if isinstance(value, Mapping): | ||
63 | origdict[key] = deep_update(origdict.get(key, {}), value) | ||
64 | |||
65 | else: | ||
66 | origdict[key] = value | ||
67 | |||
68 | return origdict | ||
diff --git a/lib/python/qmk/keyboard.py b/lib/python/qmk/keyboard.py index a4c287375..89f9346c4 100644 --- a/lib/python/qmk/keyboard.py +++ b/lib/python/qmk/keyboard.py | |||
@@ -7,7 +7,9 @@ import os | |||
7 | from glob import glob | 7 | from glob import glob |
8 | 8 | ||
9 | from qmk.c_parse import parse_config_h_file | 9 | from qmk.c_parse import parse_config_h_file |
10 | from qmk.json_schema import json_load | ||
10 | from qmk.makefile import parse_rules_mk_file | 11 | from qmk.makefile import parse_rules_mk_file |
12 | from qmk.path import is_keyboard | ||
11 | 13 | ||
12 | BOX_DRAWING_CHARACTERS = { | 14 | BOX_DRAWING_CHARACTERS = { |
13 | "unicode": { | 15 | "unicode": { |
@@ -31,6 +33,28 @@ BOX_DRAWING_CHARACTERS = { | |||
31 | base_path = os.path.join(os.getcwd(), "keyboards") + os.path.sep | 33 | base_path = os.path.join(os.getcwd(), "keyboards") + os.path.sep |
32 | 34 | ||
33 | 35 | ||
36 | def keyboard_folder(keyboard): | ||
37 | """Returns the actual keyboard folder. | ||
38 | |||
39 | This checks aliases and DEFAULT_FOLDER to resolve the actual path for a keyboard. | ||
40 | """ | ||
41 | aliases = json_load(Path('data/mappings/keyboard_aliases.json')) | ||
42 | |||
43 | if keyboard in aliases: | ||
44 | keyboard = aliases[keyboard].get('target', keyboard) | ||
45 | |||
46 | rules_mk_file = Path(base_path, keyboard, 'rules.mk') | ||
47 | |||
48 | if rules_mk_file.exists(): | ||
49 | rules_mk = parse_rules_mk_file(rules_mk_file) | ||
50 | keyboard = rules_mk.get('DEFAULT_FOLDER', keyboard) | ||
51 | |||
52 | if not is_keyboard(keyboard): | ||
53 | raise ValueError(f'Invalid keyboard: {keyboard}') | ||
54 | |||
55 | return keyboard | ||
56 | |||
57 | |||
34 | def _find_name(path): | 58 | def _find_name(path): |
35 | """Determine the keyboard name by stripping off the base_path and rules.mk. | 59 | """Determine the keyboard name by stripping off the base_path and rules.mk. |
36 | """ | 60 | """ |
diff --git a/lib/python/qmk/path.py b/lib/python/qmk/path.py index 2aa1916f5..72bae5927 100644 --- a/lib/python/qmk/path.py +++ b/lib/python/qmk/path.py | |||
@@ -15,6 +15,7 @@ def is_keyboard(keyboard_name): | |||
15 | if keyboard_name: | 15 | if keyboard_name: |
16 | keyboard_path = QMK_FIRMWARE / 'keyboards' / keyboard_name | 16 | keyboard_path = QMK_FIRMWARE / 'keyboards' / keyboard_name |
17 | rules_mk = keyboard_path / 'rules.mk' | 17 | rules_mk = keyboard_path / 'rules.mk' |
18 | |||
18 | return rules_mk.exists() | 19 | return rules_mk.exists() |
19 | 20 | ||
20 | 21 | ||
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py index bfecebdd7..b16777e54 100644 --- a/lib/python/qmk/tests/test_cli_commands.py +++ b/lib/python/qmk/tests/test_cli_commands.py | |||
@@ -134,8 +134,8 @@ def test_list_keymaps_vendor_kb_rev(): | |||
134 | 134 | ||
135 | def test_list_keymaps_no_keyboard_found(): | 135 | def test_list_keymaps_no_keyboard_found(): |
136 | result = check_subcommand('list-keymaps', '-kb', 'asdfghjkl') | 136 | result = check_subcommand('list-keymaps', '-kb', 'asdfghjkl') |
137 | check_returncode(result, [1]) | 137 | check_returncode(result, [2]) |
138 | assert 'does not exist' in result.stdout | 138 | assert 'invalid keyboard_folder value' in result.stdout |
139 | 139 | ||
140 | 140 | ||
141 | def test_json2c(): | 141 | def test_json2c(): |