diff options
| author | Nick Brassel <nick@tzarc.org> | 2023-11-28 07:53:43 +1100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-28 07:53:43 +1100 |
| commit | 5501e804ff8d41ce656061b91896c4ac8c681d78 (patch) | |
| tree | 6a655fbceaeab67cf727dbe4318721407dd31824 /lib/python/qmk/cli | |
| parent | 094357c40347e8a5db36578851f1af34a92e9f68 (diff) | |
QMK Userspace (#22222)
Co-authored-by: Duncan Sutherland <dunk2k_2000@hotmail.com>
Diffstat (limited to 'lib/python/qmk/cli')
| -rw-r--r-- | lib/python/qmk/cli/__init__.py | 5 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/compile.py | 4 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/doctor/main.py | 25 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/format/json.py | 70 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/mass_compile.py | 2 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/new/keymap.py | 8 | ||||
| -rw-r--r-- | lib/python/qmk/cli/userspace/__init__.py | 5 | ||||
| -rw-r--r-- | lib/python/qmk/cli/userspace/add.py | 51 | ||||
| -rw-r--r-- | lib/python/qmk/cli/userspace/compile.py | 38 | ||||
| -rw-r--r-- | lib/python/qmk/cli/userspace/doctor.py | 11 | ||||
| -rw-r--r-- | lib/python/qmk/cli/userspace/list.py | 51 | ||||
| -rw-r--r-- | lib/python/qmk/cli/userspace/remove.py | 37 |
12 files changed, 282 insertions, 25 deletions
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 695a180066..cf60903687 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py | |||
| @@ -81,6 +81,11 @@ subcommands = [ | |||
| 81 | 'qmk.cli.new.keymap', | 81 | 'qmk.cli.new.keymap', |
| 82 | 'qmk.cli.painter', | 82 | 'qmk.cli.painter', |
| 83 | 'qmk.cli.pytest', | 83 | 'qmk.cli.pytest', |
| 84 | 'qmk.cli.userspace.add', | ||
| 85 | 'qmk.cli.userspace.compile', | ||
| 86 | 'qmk.cli.userspace.doctor', | ||
| 87 | 'qmk.cli.userspace.list', | ||
| 88 | 'qmk.cli.userspace.remove', | ||
| 84 | 'qmk.cli.via2json', | 89 | 'qmk.cli.via2json', |
| 85 | ] | 90 | ] |
| 86 | 91 | ||
diff --git a/lib/python/qmk/cli/compile.py b/lib/python/qmk/cli/compile.py index 71c1dec162..3c8f3664ea 100755 --- a/lib/python/qmk/cli/compile.py +++ b/lib/python/qmk/cli/compile.py | |||
| @@ -37,7 +37,9 @@ def compile(cli): | |||
| 37 | from .mass_compile import mass_compile | 37 | from .mass_compile import mass_compile |
| 38 | cli.args.builds = [] | 38 | cli.args.builds = [] |
| 39 | cli.args.filter = [] | 39 | cli.args.filter = [] |
| 40 | cli.args.no_temp = False | 40 | cli.config.mass_compile.keymap = cli.config.compile.keymap |
| 41 | cli.config.mass_compile.parallel = cli.config.compile.parallel | ||
| 42 | cli.config.mass_compile.no_temp = False | ||
| 41 | return mass_compile(cli) | 43 | return mass_compile(cli) |
| 42 | 44 | ||
| 43 | # Build the environment vars | 45 | # Build the environment vars |
diff --git a/lib/python/qmk/cli/doctor/main.py b/lib/python/qmk/cli/doctor/main.py index 6a6feb87d1..dd8b58b2c7 100755 --- a/lib/python/qmk/cli/doctor/main.py +++ b/lib/python/qmk/cli/doctor/main.py | |||
| @@ -9,10 +9,11 @@ from milc import cli | |||
| 9 | from milc.questions import yesno | 9 | from milc.questions import yesno |
| 10 | 10 | ||
| 11 | from qmk import submodules | 11 | from qmk import submodules |
| 12 | from qmk.constants import QMK_FIRMWARE, QMK_FIRMWARE_UPSTREAM | 12 | from qmk.constants import QMK_FIRMWARE, QMK_FIRMWARE_UPSTREAM, QMK_USERSPACE, HAS_QMK_USERSPACE |
| 13 | from .check import CheckStatus, check_binaries, check_binary_versions, check_submodules | 13 | from .check import CheckStatus, check_binaries, check_binary_versions, check_submodules |
| 14 | from qmk.git import git_check_repo, git_get_branch, git_get_tag, git_get_last_log_entry, git_get_common_ancestor, git_is_dirty, git_get_remotes, git_check_deviation | 14 | from qmk.git import git_check_repo, git_get_branch, git_get_tag, git_get_last_log_entry, git_get_common_ancestor, git_is_dirty, git_get_remotes, git_check_deviation |
| 15 | from qmk.commands import in_virtualenv | 15 | from qmk.commands import in_virtualenv |
| 16 | from qmk.userspace import qmk_userspace_paths, qmk_userspace_validate, UserspaceValidationError | ||
| 16 | 17 | ||
| 17 | 18 | ||
| 18 | def os_tests(): | 19 | def os_tests(): |
| @@ -92,6 +93,25 @@ def output_submodule_status(): | |||
| 92 | cli.log.error(f'- {sub_name}: <<< missing or unknown >>>') | 93 | cli.log.error(f'- {sub_name}: <<< missing or unknown >>>') |
| 93 | 94 | ||
| 94 | 95 | ||
| 96 | def userspace_tests(qmk_firmware): | ||
| 97 | if qmk_firmware: | ||
| 98 | cli.log.info(f'QMK home: {{fg_cyan}}{qmk_firmware}') | ||
| 99 | |||
| 100 | for path in qmk_userspace_paths(): | ||
| 101 | try: | ||
| 102 | qmk_userspace_validate(path) | ||
| 103 | cli.log.info(f'Testing userspace candidate: {{fg_cyan}}{path}{{fg_reset}} -- {{fg_green}}Valid `qmk.json`') | ||
| 104 | except FileNotFoundError: | ||
| 105 | cli.log.warn(f'Testing userspace candidate: {{fg_cyan}}{path}{{fg_reset}} -- {{fg_red}}Missing `qmk.json`') | ||
| 106 | except UserspaceValidationError as err: | ||
| 107 | cli.log.warn(f'Testing userspace candidate: {{fg_cyan}}{path}{{fg_reset}} -- {{fg_red}}Invalid `qmk.json`') | ||
| 108 | cli.log.warn(f' -- {{fg_cyan}}{path}/qmk.json{{fg_reset}} validation error: {err}') | ||
| 109 | |||
| 110 | if QMK_USERSPACE is not None: | ||
| 111 | cli.log.info(f'QMK userspace: {{fg_cyan}}{QMK_USERSPACE}') | ||
| 112 | cli.log.info(f'Userspace enabled: {{fg_cyan}}{HAS_QMK_USERSPACE}') | ||
| 113 | |||
| 114 | |||
| 95 | @cli.argument('-y', '--yes', action='store_true', arg_only=True, help='Answer yes to all questions.') | 115 | @cli.argument('-y', '--yes', action='store_true', arg_only=True, help='Answer yes to all questions.') |
| 96 | @cli.argument('-n', '--no', action='store_true', arg_only=True, help='Answer no to all questions.') | 116 | @cli.argument('-n', '--no', action='store_true', arg_only=True, help='Answer no to all questions.') |
| 97 | @cli.subcommand('Basic QMK environment checks') | 117 | @cli.subcommand('Basic QMK environment checks') |
| @@ -108,6 +128,9 @@ def doctor(cli): | |||
| 108 | cli.log.info('QMK home: {fg_cyan}%s', QMK_FIRMWARE) | 128 | cli.log.info('QMK home: {fg_cyan}%s', QMK_FIRMWARE) |
| 109 | 129 | ||
| 110 | status = os_status = os_tests() | 130 | status = os_status = os_tests() |
| 131 | |||
| 132 | userspace_tests(None) | ||
| 133 | |||
| 111 | git_status = git_tests() | 134 | git_status = git_tests() |
| 112 | 135 | ||
| 113 | if git_status == CheckStatus.ERROR or (os_status == CheckStatus.OK and git_status == CheckStatus.WARNING): | 136 | if git_status == CheckStatus.ERROR or (os_status == CheckStatus.OK and git_status == CheckStatus.WARNING): |
diff --git a/lib/python/qmk/cli/format/json.py b/lib/python/qmk/cli/format/json.py index 3299a0d807..283513254c 100755 --- a/lib/python/qmk/cli/format/json.py +++ b/lib/python/qmk/cli/format/json.py | |||
| @@ -9,48 +9,74 @@ from milc import cli | |||
| 9 | 9 | ||
| 10 | from qmk.info import info_json | 10 | from qmk.info import info_json |
| 11 | from qmk.json_schema import json_load, validate | 11 | from qmk.json_schema import json_load, validate |
| 12 | from qmk.json_encoders import InfoJSONEncoder, KeymapJSONEncoder | 12 | from qmk.json_encoders import InfoJSONEncoder, KeymapJSONEncoder, UserspaceJSONEncoder |
| 13 | from qmk.path import normpath | 13 | from qmk.path import normpath |
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | @cli.argument('json_file', arg_only=True, type=normpath, help='JSON file to format') | 16 | def _detect_json_format(file, json_data): |
| 17 | @cli.argument('-f', '--format', choices=['auto', 'keyboard', 'keymap'], default='auto', arg_only=True, help='JSON formatter to use (Default: autodetect)') | 17 | """Detect the format of a json file. |
| 18 | @cli.argument('-i', '--inplace', action='store_true', arg_only=True, help='If set, will operate in-place on the input file') | ||
| 19 | @cli.argument('-p', '--print', action='store_true', arg_only=True, help='If set, will print the formatted json to stdout ') | ||
| 20 | @cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True) | ||
| 21 | def format_json(cli): | ||
| 22 | """Format a json file. | ||
| 23 | """ | 18 | """ |
| 24 | json_file = json_load(cli.args.json_file) | 19 | json_encoder = None |
| 25 | 20 | try: | |
| 26 | if cli.args.format == 'auto': | 21 | validate(json_data, 'qmk.user_repo.v1') |
| 22 | json_encoder = UserspaceJSONEncoder | ||
| 23 | except ValidationError: | ||
| 24 | pass | ||
| 25 | |||
| 26 | if json_encoder is None: | ||
| 27 | try: | 27 | try: |
| 28 | validate(json_file, 'qmk.keyboard.v1') | 28 | validate(json_data, 'qmk.keyboard.v1') |
| 29 | json_encoder = InfoJSONEncoder | 29 | json_encoder = InfoJSONEncoder |
| 30 | |||
| 31 | except ValidationError as e: | 30 | except ValidationError as e: |
| 32 | cli.log.warning('File %s did not validate as a keyboard:\n\t%s', cli.args.json_file, e) | 31 | cli.log.warning('File %s did not validate as a keyboard info.json or userspace qmk.json:\n\t%s', file, e) |
| 33 | cli.log.info('Treating %s as a keymap file.', cli.args.json_file) | 32 | cli.log.info('Treating %s as a keymap file.', file) |
| 34 | json_encoder = KeymapJSONEncoder | 33 | json_encoder = KeymapJSONEncoder |
| 34 | |||
| 35 | return json_encoder | ||
| 36 | |||
| 37 | |||
| 38 | def _get_json_encoder(file, json_data): | ||
| 39 | """Get the json encoder for a file. | ||
| 40 | """ | ||
| 41 | json_encoder = None | ||
| 42 | if cli.args.format == 'auto': | ||
| 43 | json_encoder = _detect_json_format(file, json_data) | ||
| 35 | elif cli.args.format == 'keyboard': | 44 | elif cli.args.format == 'keyboard': |
| 36 | json_encoder = InfoJSONEncoder | 45 | json_encoder = InfoJSONEncoder |
| 37 | elif cli.args.format == 'keymap': | 46 | elif cli.args.format == 'keymap': |
| 38 | json_encoder = KeymapJSONEncoder | 47 | json_encoder = KeymapJSONEncoder |
| 48 | elif cli.args.format == 'userspace': | ||
| 49 | json_encoder = UserspaceJSONEncoder | ||
| 39 | else: | 50 | else: |
| 40 | # This should be impossible | 51 | # This should be impossible |
| 41 | cli.log.error('Unknown format: %s', cli.args.format) | 52 | cli.log.error('Unknown format: %s', cli.args.format) |
| 53 | return json_encoder | ||
| 54 | |||
| 55 | |||
| 56 | @cli.argument('json_file', arg_only=True, type=normpath, help='JSON file to format') | ||
| 57 | @cli.argument('-f', '--format', choices=['auto', 'keyboard', 'keymap', 'userspace'], default='auto', arg_only=True, help='JSON formatter to use (Default: autodetect)') | ||
| 58 | @cli.argument('-i', '--inplace', action='store_true', arg_only=True, help='If set, will operate in-place on the input file') | ||
| 59 | @cli.argument('-p', '--print', action='store_true', arg_only=True, help='If set, will print the formatted json to stdout ') | ||
| 60 | @cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True) | ||
| 61 | def format_json(cli): | ||
| 62 | """Format a json file. | ||
| 63 | """ | ||
| 64 | json_data = json_load(cli.args.json_file) | ||
| 65 | |||
| 66 | json_encoder = _get_json_encoder(cli.args.json_file, json_data) | ||
| 67 | if json_encoder is None: | ||
| 42 | return False | 68 | return False |
| 43 | 69 | ||
| 44 | if json_encoder == KeymapJSONEncoder and 'layout' in json_file: | 70 | if json_encoder == KeymapJSONEncoder and 'layout' in json_data: |
| 45 | # Attempt to format the keycodes. | 71 | # Attempt to format the keycodes. |
| 46 | layout = json_file['layout'] | 72 | layout = json_data['layout'] |
| 47 | info_data = info_json(json_file['keyboard']) | 73 | info_data = info_json(json_data['keyboard']) |
| 48 | 74 | ||
| 49 | if layout in info_data.get('layout_aliases', {}): | 75 | if layout in info_data.get('layout_aliases', {}): |
| 50 | layout = json_file['layout'] = info_data['layout_aliases'][layout] | 76 | layout = json_data['layout'] = info_data['layout_aliases'][layout] |
| 51 | 77 | ||
| 52 | if layout in info_data.get('layouts'): | 78 | if layout in info_data.get('layouts'): |
| 53 | for layer_num, layer in enumerate(json_file['layers']): | 79 | for layer_num, layer in enumerate(json_data['layers']): |
| 54 | current_layer = [] | 80 | current_layer = [] |
| 55 | last_row = 0 | 81 | last_row = 0 |
| 56 | 82 | ||
| @@ -61,9 +87,9 @@ def format_json(cli): | |||
| 61 | 87 | ||
| 62 | current_layer.append(keymap_key) | 88 | current_layer.append(keymap_key) |
| 63 | 89 | ||
| 64 | json_file['layers'][layer_num] = current_layer | 90 | json_data['layers'][layer_num] = current_layer |
| 65 | 91 | ||
| 66 | output = json.dumps(json_file, cls=json_encoder, sort_keys=True) | 92 | output = json.dumps(json_data, cls=json_encoder, sort_keys=True) |
| 67 | 93 | ||
| 68 | if cli.args.inplace: | 94 | if cli.args.inplace: |
| 69 | with open(cli.args.json_file, 'w+', encoding='utf-8') as outfile: | 95 | with open(cli.args.json_file, 'w+', encoding='utf-8') as outfile: |
diff --git a/lib/python/qmk/cli/mass_compile.py b/lib/python/qmk/cli/mass_compile.py index 7968de53e7..b025f85701 100755 --- a/lib/python/qmk/cli/mass_compile.py +++ b/lib/python/qmk/cli/mass_compile.py | |||
| @@ -72,7 +72,7 @@ all: {keyboard_safe}_{keymap_name}_binary | |||
| 72 | # yapf: enable | 72 | # yapf: enable |
| 73 | f.write('\n') | 73 | f.write('\n') |
| 74 | 74 | ||
| 75 | cli.run([make_cmd, *get_make_parallel_args(parallel), '-f', makefile.as_posix(), 'all'], capture_output=False, stdin=DEVNULL) | 75 | cli.run([find_make(), *get_make_parallel_args(parallel), '-f', makefile.as_posix(), 'all'], capture_output=False, stdin=DEVNULL) |
| 76 | 76 | ||
| 77 | # Check for failures | 77 | # Check for failures |
| 78 | failures = [f for f in builddir.glob(f'failed.log.{os.getpid()}.*')] | 78 | failures = [f for f in builddir.glob(f'failed.log.{os.getpid()}.*')] |
diff --git a/lib/python/qmk/cli/new/keymap.py b/lib/python/qmk/cli/new/keymap.py index 9b0ac221a4..d4339bc9ef 100755 --- a/lib/python/qmk/cli/new/keymap.py +++ b/lib/python/qmk/cli/new/keymap.py | |||
| @@ -5,10 +5,12 @@ import shutil | |||
| 5 | from milc import cli | 5 | from milc import cli |
| 6 | from milc.questions import question | 6 | from milc.questions import question |
| 7 | 7 | ||
| 8 | from qmk.constants import HAS_QMK_USERSPACE, QMK_USERSPACE | ||
| 8 | from qmk.path import is_keyboard, keymaps, keymap | 9 | from qmk.path import is_keyboard, keymaps, keymap |
| 9 | from qmk.git import git_get_username | 10 | from qmk.git import git_get_username |
| 10 | from qmk.decorators import automagic_keyboard, automagic_keymap | 11 | from qmk.decorators import automagic_keyboard, automagic_keymap |
| 11 | from qmk.keyboard import keyboard_completer, keyboard_folder | 12 | from qmk.keyboard import keyboard_completer, keyboard_folder |
| 13 | from qmk.userspace import UserspaceDefs | ||
| 12 | 14 | ||
| 13 | 15 | ||
| 14 | def prompt_keyboard(): | 16 | def prompt_keyboard(): |
| @@ -68,3 +70,9 @@ def new_keymap(cli): | |||
| 68 | # end message to user | 70 | # end message to user |
| 69 | cli.log.info(f'{{fg_green}}Created a new keymap called {{fg_cyan}}{user_name}{{fg_green}} in: {{fg_cyan}}{keymap_path_new}.{{fg_reset}}') | 71 | cli.log.info(f'{{fg_green}}Created a new keymap called {{fg_cyan}}{user_name}{{fg_green}} in: {{fg_cyan}}{keymap_path_new}.{{fg_reset}}') |
| 70 | cli.log.info(f"Compile a firmware with your new keymap by typing: {{fg_yellow}}qmk compile -kb {kb_name} -km {user_name}{{fg_reset}}.") | 72 | cli.log.info(f"Compile a firmware with your new keymap by typing: {{fg_yellow}}qmk compile -kb {kb_name} -km {user_name}{{fg_reset}}.") |
| 73 | |||
| 74 | # Add to userspace compile if we have userspace available | ||
| 75 | if HAS_QMK_USERSPACE: | ||
| 76 | userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json') | ||
| 77 | userspace.add_target(keyboard=kb_name, keymap=user_name, do_print=False) | ||
| 78 | return userspace.save() | ||
diff --git a/lib/python/qmk/cli/userspace/__init__.py b/lib/python/qmk/cli/userspace/__init__.py new file mode 100644 index 0000000000..5757d3a4c9 --- /dev/null +++ b/lib/python/qmk/cli/userspace/__init__.py | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | from . import doctor | ||
| 2 | from . import add | ||
| 3 | from . import remove | ||
| 4 | from . import list | ||
| 5 | from . import compile | ||
diff --git a/lib/python/qmk/cli/userspace/add.py b/lib/python/qmk/cli/userspace/add.py new file mode 100644 index 0000000000..8993d54dba --- /dev/null +++ b/lib/python/qmk/cli/userspace/add.py | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | # Copyright 2023 Nick Brassel (@tzarc) | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | from pathlib import Path | ||
| 4 | from milc import cli | ||
| 5 | |||
| 6 | from qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE | ||
| 7 | from qmk.keyboard import keyboard_completer, keyboard_folder_or_all | ||
| 8 | from qmk.keymap import keymap_completer, is_keymap_target | ||
| 9 | from qmk.userspace import UserspaceDefs | ||
| 10 | |||
| 11 | |||
| 12 | @cli.argument('builds', nargs='*', arg_only=True, help="List of builds in form <keyboard>:<keymap>, or path to a keymap JSON file.") | ||
| 13 | @cli.argument('-kb', '--keyboard', type=keyboard_folder_or_all, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.') | ||
| 14 | @cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') | ||
| 15 | @cli.subcommand('Adds a build target to userspace `qmk.json`.') | ||
| 16 | def userspace_add(cli): | ||
| 17 | if not HAS_QMK_USERSPACE: | ||
| 18 | cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.') | ||
| 19 | return False | ||
| 20 | |||
| 21 | userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json') | ||
| 22 | |||
| 23 | if len(cli.args.builds) > 0: | ||
| 24 | json_like_targets = list([Path(p) for p in filter(lambda e: Path(e).exists() and Path(e).suffix == '.json', cli.args.builds)]) | ||
| 25 | make_like_targets = list(filter(lambda e: Path(e) not in json_like_targets, cli.args.builds)) | ||
| 26 | |||
| 27 | for e in json_like_targets: | ||
| 28 | userspace.add_target(json_path=e) | ||
| 29 | |||
| 30 | for e in make_like_targets: | ||
| 31 | s = e.split(':') | ||
| 32 | userspace.add_target(keyboard=s[0], keymap=s[1]) | ||
| 33 | |||
| 34 | else: | ||
| 35 | failed = False | ||
| 36 | try: | ||
| 37 | if not is_keymap_target(cli.args.keyboard, cli.args.keymap): | ||
| 38 | failed = True | ||
| 39 | except KeyError: | ||
| 40 | failed = True | ||
| 41 | |||
| 42 | if failed: | ||
| 43 | from qmk.cli.new.keymap import new_keymap | ||
| 44 | cli.config.new_keymap.keyboard = cli.args.keyboard | ||
| 45 | cli.config.new_keymap.keymap = cli.args.keymap | ||
| 46 | if new_keymap(cli) is not False: | ||
| 47 | userspace.add_target(keyboard=cli.args.keyboard, keymap=cli.args.keymap) | ||
| 48 | else: | ||
| 49 | userspace.add_target(keyboard=cli.args.keyboard, keymap=cli.args.keymap) | ||
| 50 | |||
| 51 | return userspace.save() | ||
diff --git a/lib/python/qmk/cli/userspace/compile.py b/lib/python/qmk/cli/userspace/compile.py new file mode 100644 index 0000000000..0a42dd5bf5 --- /dev/null +++ b/lib/python/qmk/cli/userspace/compile.py | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | # Copyright 2023 Nick Brassel (@tzarc) | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | from pathlib import Path | ||
| 4 | from milc import cli | ||
| 5 | |||
| 6 | from qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE | ||
| 7 | from qmk.commands import build_environment | ||
| 8 | from qmk.userspace import UserspaceDefs | ||
| 9 | from qmk.build_targets import JsonKeymapBuildTarget | ||
| 10 | from qmk.search import search_keymap_targets | ||
| 11 | from qmk.cli.mass_compile import mass_compile_targets | ||
| 12 | |||
| 13 | |||
| 14 | @cli.argument('-t', '--no-temp', arg_only=True, action='store_true', help="Remove temporary files during build.") | ||
| 15 | @cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.") | ||
| 16 | @cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.") | ||
| 17 | @cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the commands to be run.") | ||
| 18 | @cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.") | ||
| 19 | @cli.subcommand('Compiles the build targets specified in userspace `qmk.json`.') | ||
| 20 | def userspace_compile(cli): | ||
| 21 | if not HAS_QMK_USERSPACE: | ||
| 22 | cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.') | ||
| 23 | return False | ||
| 24 | |||
| 25 | userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json') | ||
| 26 | |||
| 27 | build_targets = [] | ||
| 28 | keyboard_keymap_targets = [] | ||
| 29 | for e in userspace.build_targets: | ||
| 30 | if isinstance(e, Path): | ||
| 31 | build_targets.append(JsonKeymapBuildTarget(e)) | ||
| 32 | elif isinstance(e, dict): | ||
| 33 | keyboard_keymap_targets.append((e['keyboard'], e['keymap'])) | ||
| 34 | |||
| 35 | if len(keyboard_keymap_targets) > 0: | ||
| 36 | build_targets.extend(search_keymap_targets(keyboard_keymap_targets)) | ||
| 37 | |||
| 38 | mass_compile_targets(list(set(build_targets)), cli.args.clean, cli.args.dry_run, cli.config.userspace_compile.no_temp, cli.config.userspace_compile.parallel, **build_environment(cli.args.env)) | ||
diff --git a/lib/python/qmk/cli/userspace/doctor.py b/lib/python/qmk/cli/userspace/doctor.py new file mode 100644 index 0000000000..2b7e29aa7e --- /dev/null +++ b/lib/python/qmk/cli/userspace/doctor.py | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # Copyright 2023 Nick Brassel (@tzarc) | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | from milc import cli | ||
| 4 | |||
| 5 | from qmk.constants import QMK_FIRMWARE | ||
| 6 | from qmk.cli.doctor.main import userspace_tests | ||
| 7 | |||
| 8 | |||
| 9 | @cli.subcommand('Checks userspace configuration.') | ||
| 10 | def userspace_doctor(cli): | ||
| 11 | userspace_tests(QMK_FIRMWARE) | ||
diff --git a/lib/python/qmk/cli/userspace/list.py b/lib/python/qmk/cli/userspace/list.py new file mode 100644 index 0000000000..a63f669dd7 --- /dev/null +++ b/lib/python/qmk/cli/userspace/list.py | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | # Copyright 2023 Nick Brassel (@tzarc) | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | from pathlib import Path | ||
| 4 | from dotty_dict import Dotty | ||
| 5 | from milc import cli | ||
| 6 | |||
| 7 | from qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE | ||
| 8 | from qmk.userspace import UserspaceDefs | ||
| 9 | from qmk.build_targets import BuildTarget | ||
| 10 | from qmk.keyboard import is_all_keyboards, keyboard_folder | ||
| 11 | from qmk.keymap import is_keymap_target | ||
| 12 | from qmk.search import search_keymap_targets | ||
| 13 | |||
| 14 | |||
| 15 | @cli.argument('-e', '--expand', arg_only=True, action='store_true', help="Expands any use of `all` for either keyboard or keymap.") | ||
| 16 | @cli.subcommand('Lists the build targets specified in userspace `qmk.json`.') | ||
| 17 | def userspace_list(cli): | ||
| 18 | if not HAS_QMK_USERSPACE: | ||
| 19 | cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.') | ||
| 20 | return False | ||
| 21 | |||
| 22 | userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json') | ||
| 23 | |||
| 24 | if cli.args.expand: | ||
| 25 | build_targets = [] | ||
| 26 | for e in userspace.build_targets: | ||
| 27 | if isinstance(e, Path): | ||
| 28 | build_targets.append(e) | ||
| 29 | elif isinstance(e, dict) or isinstance(e, Dotty): | ||
| 30 | build_targets.extend(search_keymap_targets([(e['keyboard'], e['keymap'])])) | ||
| 31 | else: | ||
| 32 | build_targets = userspace.build_targets | ||
| 33 | |||
| 34 | for e in build_targets: | ||
| 35 | if isinstance(e, Path): | ||
| 36 | # JSON keymap from userspace | ||
| 37 | cli.log.info(f'JSON keymap: {{fg_cyan}}{e}{{fg_reset}}') | ||
| 38 | continue | ||
| 39 | elif isinstance(e, dict) or isinstance(e, Dotty): | ||
| 40 | # keyboard/keymap dict from userspace | ||
| 41 | keyboard = e['keyboard'] | ||
| 42 | keymap = e['keymap'] | ||
| 43 | elif isinstance(e, BuildTarget): | ||
| 44 | # BuildTarget from search_keymap_targets() | ||
| 45 | keyboard = e.keyboard | ||
| 46 | keymap = e.keymap | ||
| 47 | |||
| 48 | if is_all_keyboards(keyboard) or is_keymap_target(keyboard_folder(keyboard), keymap): | ||
| 49 | cli.log.info(f'Keyboard: {{fg_cyan}}{keyboard}{{fg_reset}}, keymap: {{fg_cyan}}{keymap}{{fg_reset}}') | ||
| 50 | else: | ||
| 51 | cli.log.warn(f'Keyboard: {{fg_cyan}}{keyboard}{{fg_reset}}, keymap: {{fg_cyan}}{keymap}{{fg_reset}} -- not found!') | ||
diff --git a/lib/python/qmk/cli/userspace/remove.py b/lib/python/qmk/cli/userspace/remove.py new file mode 100644 index 0000000000..c7d180bfd1 --- /dev/null +++ b/lib/python/qmk/cli/userspace/remove.py | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | # Copyright 2023 Nick Brassel (@tzarc) | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | from pathlib import Path | ||
| 4 | from milc import cli | ||
| 5 | |||
| 6 | from qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE | ||
| 7 | from qmk.keyboard import keyboard_completer, keyboard_folder_or_all | ||
| 8 | from qmk.keymap import keymap_completer | ||
| 9 | from qmk.userspace import UserspaceDefs | ||
| 10 | |||
| 11 | |||
| 12 | @cli.argument('builds', nargs='*', arg_only=True, help="List of builds in form <keyboard>:<keymap>, or path to a keymap JSON file.") | ||
| 13 | @cli.argument('-kb', '--keyboard', type=keyboard_folder_or_all, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.') | ||
| 14 | @cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') | ||
| 15 | @cli.subcommand('Removes a build target from userspace `qmk.json`.') | ||
| 16 | def userspace_remove(cli): | ||
| 17 | if not HAS_QMK_USERSPACE: | ||
| 18 | cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.') | ||
| 19 | return False | ||
| 20 | |||
| 21 | userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json') | ||
| 22 | |||
| 23 | if len(cli.args.builds) > 0: | ||
| 24 | json_like_targets = list([Path(p) for p in filter(lambda e: Path(e).exists() and Path(e).suffix == '.json', cli.args.builds)]) | ||
| 25 | make_like_targets = list(filter(lambda e: Path(e) not in json_like_targets, cli.args.builds)) | ||
| 26 | |||
| 27 | for e in json_like_targets: | ||
| 28 | userspace.remove_target(json_path=e) | ||
| 29 | |||
| 30 | for e in make_like_targets: | ||
| 31 | s = e.split(':') | ||
| 32 | userspace.remove_target(keyboard=s[0], keymap=s[1]) | ||
| 33 | |||
| 34 | else: | ||
| 35 | userspace.remove_target(keyboard=cli.args.keyboard, keymap=cli.args.keymap) | ||
| 36 | |||
| 37 | return userspace.save() | ||
