Add CLI command for flashing a keyboard

A new CLI subcommand was added, flash, which behaves very similar to the already present compile CLI comamnd, but with the added ability to target a bootloader. The command is used like so: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename].

A -kb <keyboard> and -km <keymap> is expected, or a configurator export JSON filename. A bootloader can be specified using -bl <target>, and if left unspecified, the target is assumed to be :flash. -bl can be used to list the available bootloaders.

If -km <keymap> is provided, but no -kb <keyboard>, then a message is printed suggesting the user to run qmk list_keyboards.
This commit is contained in:
jorgemanzo 2019-10-04 23:38:34 -07:00 committed by skullY
parent 4f5b34af56
commit 897888db41
6 changed files with 181 additions and 5 deletions

View File

@ -95,6 +95,30 @@ qmk compile <configuratorExport.json>
qmk compile -kb <keyboard_name> -km <keymap_name> qmk compile -kb <keyboard_name> -km <keymap_name>
``` ```
## `qmk flash`
This command is similar to `qmk compile`, but can also target a bootloader. The bootloader is optional, and is set to `:flash` by default.
To specify a different bootloader, use `-bl <bootloader>`. Visit <https://docs.qmk.fm/#/flashing>
for more details of the available bootloaders.
**Usage for Configurator Exports**:
```
qmk flash <configuratorExport.json> -bl <bootloader>
```
**Usage for Keymaps**:
```
qmk flash -kb <keyboard_name> -km <keymap_name> -bl <bootloader>
```
**Listing the Bootloaders**
```
qmk flash -b
```
## `qmk config` ## `qmk config`
This command lets you configure the behavior of QMK. For the full `qmk config` documentation see [CLI Configuration](cli_configuration.md). This command lets you configure the behavior of QMK. For the full `qmk config` documentation see [CLI Configuration](cli_configuration.md).

View File

@ -7,6 +7,7 @@ from . import compile
from . import config from . import config
from . import docs from . import docs
from . import doctor from . import doctor
from . import flash
from . import hello from . import hello
from . import json from . import json
from . import list from . import list

View File

@ -9,6 +9,9 @@ import subprocess
from argparse import FileType from argparse import FileType
from milc import cli from milc import cli
from qmk.commands import create_make_command
from qmk.commands import parse_configurator_json
from qmk.commands import compile_configurator_json
import qmk.keymap import qmk.keymap
import qmk.path import qmk.path
@ -30,20 +33,21 @@ def compile(cli):
""" """
if cli.args.filename: if cli.args.filename:
# Parse the configurator json # Parse the configurator json
user_keymap = json.load(cli.args.filename) user_keymap = parse_configurator_json(cli.args.filename)
# Generate the keymap # Generate the keymap
keymap_path = qmk.path.keymap(user_keymap['keyboard']) keymap_path = qmk.path.keymap(user_keymap['keyboard'])
cli.log.info('Creating {fg_cyan}%s{style_reset_all} keymap in {fg_cyan}%s', user_keymap['keymap'], keymap_path) cli.log.info('Creating {fg_cyan}%s{style_reset_all} keymap in {fg_cyan}%s', user_keymap['keymap'], keymap_path)
qmk.keymap.write(user_keymap['keyboard'], user_keymap['keymap'], user_keymap['layout'], user_keymap['layers'])
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
# Compile the keymap # Compile the keymap
command = ['make', ':'.join((user_keymap['keyboard'], user_keymap['keymap']))] command = compile_configurator_json(cli.args.filename)
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
elif cli.config.compile.keyboard and cli.config.compile.keymap: elif cli.config.compile.keyboard and cli.config.compile.keymap:
# Generate the make command for a specific keyboard/keymap. # Generate the make command for a specific keyboard/keymap.
command = ['make', ':'.join((cli.config.compile.keyboard, cli.config.compile.keymap))] command = create_make_command(cli.config.compile.keyboard, cli.config.compile.keymap)
else: else:
cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`.') cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`.')

View File

@ -0,0 +1,87 @@
"""Compile and flash QMK Firmware
You can compile a keymap already in the repo or using a QMK Configurator export.
A bootloader must be specified.
"""
import os
import sys
import subprocess
from argparse import FileType
from milc import cli
from qmk.commands import create_make_command
from qmk.commands import parse_configurator_json
from qmk.commands import compile_configurator_json
import qmk.path
def print_bootloader_help():
"""Prints the available bootloaders listed in docs.qmk.fm.
"""
cli.log.info('Here are the available bootloaders:')
cli.echo('\tdfu')
cli.echo('\tdfu-ee')
cli.echo('\tdfu-split-left')
cli.echo('\tdfu-split-right')
cli.echo('\tavrdude')
cli.echo('\tBootloadHID')
cli.echo('\tdfu-util')
cli.echo('\tdfu-util-split-left')
cli.echo('\tdfu-util-split-right')
cli.echo('\tst-link-cli')
cli.echo('For more info, visit https://docs.qmk.fm/#/flashing')
@cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.')
@cli.argument('filename', nargs='?', arg_only=True, help='The configurator export JSON to compile. Use this if you dont want to specify a keymap and keyboard.')
@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.')
@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.')
@cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.')
@cli.subcommand('QMK Flash.')
def flash(cli):
"""Compile and or flash QMK Firmware or keyboard/layout
If a Configurator JSON export is supplied this command will create a new keymap. Keymap and Keyboard arguments
will be ignored.
If no file is supplied, keymap and keyboard are expected.
If bootloader is omitted, the one according to the rules.mk will be used.
"""
command = []
if cli.args.bootloaders:
# Provide usage and list bootloaders
cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
print_bootloader_help()
return False
elif cli.args.keymap and not cli.args.keyboard:
# If only a keymap was given but no keyboard, suggest listing keyboards
cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
cli.log.error('run \'qmk list_keyboards\' to find out the supported keyboards')
return False
elif cli.args.filename:
# Get keymap path to log info
user_keymap = parse_configurator_json(cli.args.filename)
keymap_path = qmk.path.keymap(user_keymap['keyboard'])
cli.log.info('Creating {fg_cyan}%s{style_reset_all} keymap in {fg_cyan}%s', user_keymap['keymap'], keymap_path)
# Convert the JSON into a C file and write it to disk.
command = compile_configurator_json(cli.args.filename, cli.args.bootloader)
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
elif cli.args.keyboard and cli.args.keymap:
# Generate the make command for a specific keyboard/keymap.
command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, cli.args.bootloader)
else:
cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`. You can also specify a bootloader with --bootloader. Use --bootloaders to list the available bootloaders.')
return False
cli.log.info('Flashing keymap with {fg_cyan}%s\n\n', ' '.join(command))
subprocess.run(command)

View File

@ -0,0 +1,57 @@
"""Functions that build make commands
"""
import json
import qmk.keymap
def create_make_command(keyboard, keymap, target=None):
"""Create a make compile command
Args:
keyboard
The path of the keyboard, for example 'plank'
keymap
The name of the keymap, for example 'algernon'
target
Usually a bootloader.
Returns:
A command that can be run to make the specified keyboard and keymap
"""
if target is None:
return ['make', ':'.join((keyboard, keymap))]
return ['make', ':'.join((keyboard, keymap, target))]
def parse_configurator_json(configurator_filename):
"""Open and parse a configurator json export
"""
file = open(configurator_filename)
user_keymap = json.load(file)
file.close()
return user_keymap
def compile_configurator_json(configurator_filename, bootloader=None):
"""Convert a configurator export JSON file into a C file
Args:
configurator_filename
The configurator JSON export file
bootloader
A bootloader to flash
Returns:
A command to run to compile and flash the C file.
"""
# Parse the configurator json
user_keymap = parse_configurator_json(configurator_filename)
# Write the keymap C file
qmk.keymap.write(user_keymap['keyboard'], user_keymap['keymap'], user_keymap['layout'], user_keymap['layers'])
# Return a command that can be run to make the keymap and flash if given
if bootloader is None:
return create_make_command(user_keymap['keyboard'], user_keymap['keymap'])
return create_make_command(user_keymap['keyboard'], user_keymap['keymap'], bootloader)

View File

@ -13,6 +13,9 @@ def test_cformat():
def test_compile(): def test_compile():
assert check_subcommand('compile', '-kb', 'handwired/onekey/pytest', '-km', 'default').returncode == 0 assert check_subcommand('compile', '-kb', 'handwired/onekey/pytest', '-km', 'default').returncode == 0
def test_flash():
assert check_subcommand('flash', '-b').returncode == 1
assert check_subcommand('flash').returncode == 1
def test_config(): def test_config():
result = check_subcommand('config') result = check_subcommand('config')