Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve argument handling of c2json #22170

Merged
merged 2 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 33 additions & 26 deletions lib/python/qmk/cli/c2json.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
"""Generate a keymap.json from a keymap.c file.
"""
import re
import json

from argcomplete.completers import FilesCompleter
from milc import cli

import qmk.keymap
import qmk.path
from qmk.json_encoders import InfoJSONEncoder
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.keymap import locate_keymap, find_keymap_from_dir, generate_json, c2json as c2json_impl
from qmk.errors import CppError
from qmk.commands import dump_lines


@cli.argument('--no-cpp', arg_only=True, action='store_false', help='Do not use \'cpp\' on keymap.c')
@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='The keyboard\'s name')
@cli.argument('-km', '--keymap', arg_only=True, required=True, help='The keymap\'s name')
@cli.argument('filename', arg_only=True, completer=FilesCompleter('.c'), help='keymap.c file')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard\'s name')
@cli.argument('-km', '--keymap', help='The keymap\'s name')
@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.c'), help='keymap.c file')
@cli.subcommand('Creates a keymap.json from a keymap.c file.')
@automagic_keyboard
@automagic_keymap
def c2json(cli):
"""Generate a keymap.json from a keymap.c file.

This command uses the `qmk.keymap` module to generate a keymap.json from a keymap.c file. The generated keymap is written to stdout, or to a file if -o is provided.
"""
if cli.args.filename != '-':
cli.args.filename = qmk.path.normpath(cli.args.filename)
filename = cli.args.filename
keyboard = cli.config.c2json.keyboard
keymap = cli.config.c2json.keymap

# Error checking
if not cli.args.filename.exists():
cli.log.error('C file does not exist!')
cli.print_usage()
return False
if filename:
if not keyboard and not keymap:
# fallback to inferring keyboard/keymap from path
(keymap, found_type) = find_keymap_from_dir(filename)
if found_type == 'keymap_directory':
keyboard = re.search(fr"keyboards/(.+)/keymaps/{keymap}/.*", filename.as_posix()).group(1)

# Environment processing
if cli.args.output == ('-'):
cli.args.output = None
elif keyboard and keymap:
if not filename:
# fallback to inferring keyboard/keymap from path
filename = locate_keymap(keyboard, keymap)

if not all((filename, keyboard, keymap)):
cli.log.error('You must supply keyboard and keymap, a path to a keymap.c within qmk_firmware, or absolute filename and keyboard and keymap')
cli.print_help()
return False

# Parse the keymap.c
try:
keymap_json = qmk.keymap.c2json(cli.args.keyboard, cli.args.keymap, cli.args.filename, use_cpp=cli.args.no_cpp)
keymap_json = c2json_impl(keyboard, keymap, filename, use_cpp=cli.args.no_cpp)
except CppError as e:
if cli.config.general.verbose:
cli.log.debug('The C pre-processor ran into a fatal error: %s', e)
Expand All @@ -48,19 +60,14 @@ def c2json(cli):

# Generate the keymap.json
try:
keymap_json = qmk.keymap.generate_json(keymap_json['keymap'], keymap_json['keyboard'], keymap_json['layout'], keymap_json['layers'])
keymap_json = generate_json(keymap_json['keymap'], keymap_json['keyboard'], keymap_json['layout'], keymap_json['layers'])
except KeyError:
cli.log.error('Something went wrong. Try to use --no-cpp.')
return False

if cli.args.output:
cli.args.output.parent.mkdir(parents=True, exist_ok=True)
if cli.args.output.exists():
cli.args.output.replace(cli.args.output.parent / (cli.args.output.name + '.bak'))
cli.args.output.write_text(json.dumps(keymap_json, cls=InfoJSONEncoder, sort_keys=True))

if not cli.args.quiet:
cli.log.info('Wrote keymap to %s.', cli.args.output)

keymap_lines = [json.dumps(keymap_json, cls=InfoJSONEncoder, sort_keys=True)]
else:
print(json.dumps(keymap_json))
keymap_lines = [json.dumps(keymap_json)]

dump_lines(cli.args.output, keymap_lines, cli.args.quiet)
23 changes: 11 additions & 12 deletions lib/python/qmk/keymap.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,16 +191,15 @@ def _strip_any(keycode):
return keycode


def find_keymap_from_dir():
"""Returns `(keymap_name, source)` for the directory we're currently in.

def find_keymap_from_dir(*args):
"""Returns `(keymap_name, source)` for the directory provided (or cwd if not specified).
"""
relative_cwd = qmk.path.under_qmk_firmware()
relative_path = qmk.path.under_qmk_firmware(*args)

if relative_cwd and len(relative_cwd.parts) > 1:
if relative_path and len(relative_path.parts) > 1:
# If we're in `qmk_firmware/keyboards` and `keymaps` is in our path, try to find the keyboard name.
if relative_cwd.parts[0] == 'keyboards' and 'keymaps' in relative_cwd.parts:
current_path = Path('/'.join(relative_cwd.parts[1:])) # Strip 'keyboards' from the front
if relative_path.parts[0] == 'keyboards' and 'keymaps' in relative_path.parts:
current_path = Path('/'.join(relative_path.parts[1:])) # Strip 'keyboards' from the front

if 'keymaps' in current_path.parts and current_path.name != 'keymaps':
while current_path.parent.name != 'keymaps':
Expand All @@ -209,13 +208,13 @@ def find_keymap_from_dir():
return current_path.name, 'keymap_directory'

# If we're in `qmk_firmware/layouts` guess the name from the community keymap they're in
elif relative_cwd.parts[0] == 'layouts' and is_keymap_dir(relative_cwd):
return relative_cwd.name, 'layouts_directory'
elif relative_path.parts[0] == 'layouts' and is_keymap_dir(relative_path):
return relative_path.name, 'layouts_directory'

# If we're in `qmk_firmware/users` guess the name from the userspace they're in
elif relative_cwd.parts[0] == 'users':
elif relative_path.parts[0] == 'users':
# Guess the keymap name based on which userspace they're in
return relative_cwd.parts[1], 'users_directory'
return relative_path.parts[1], 'users_directory'

return None, None

Expand Down Expand Up @@ -657,7 +656,7 @@ def parse_keymap_c(keymap_file, use_cpp=True):
Returns:
a dictionary containing the parsed keymap
"""
if keymap_file == '-':
if not isinstance(keymap_file, (Path, str)) or keymap_file == '-':
if use_cpp:
keymap_file = _c_preprocess(None, sys.stdin)
else:
Expand Down
6 changes: 2 additions & 4 deletions lib/python/qmk/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,11 @@ def is_keyboard(keyboard_name):
return rules_mk.exists()


def under_qmk_firmware():
def under_qmk_firmware(path=Path(os.environ['ORIG_CWD'])):
"""Returns a Path object representing the relative path under qmk_firmware, or None.
"""
cwd = Path(os.environ['ORIG_CWD'])

try:
return cwd.relative_to(QMK_FIRMWARE)
return path.relative_to(QMK_FIRMWARE)
except ValueError:
return None

Expand Down