summaryrefslogtreecommitdiff
path: root/lib/python/qmk
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python/qmk')
-rw-r--r--lib/python/qmk/c_parse.py2
-rw-r--r--lib/python/qmk/cli/__init__.py141
-rw-r--r--lib/python/qmk/cli/chibios/__init__.py1
-rw-r--r--lib/python/qmk/cli/console.py302
-rw-r--r--lib/python/qmk/cli/format/__init__.py1
-rw-r--r--lib/python/qmk/cli/generate/__init__.py9
-rwxr-xr-xlib/python/qmk/cli/generate/layouts.py4
-rw-r--r--lib/python/qmk/cli/list/__init__.py2
-rwxr-xr-xlib/python/qmk/cli/multibuild.py37
-rw-r--r--lib/python/qmk/cli/new/__init__.py2
-rw-r--r--lib/python/qmk/commands.py1
-rw-r--r--lib/python/qmk/constants.py2
-rw-r--r--lib/python/qmk/decorators.py12
-rw-r--r--lib/python/qmk/tests/test_cli_commands.py4
14 files changed, 428 insertions, 92 deletions
diff --git a/lib/python/qmk/c_parse.py b/lib/python/qmk/c_parse.py
index d4f39c8839..991373d569 100644
--- a/lib/python/qmk/c_parse.py
+++ b/lib/python/qmk/c_parse.py
@@ -8,7 +8,7 @@ from milc import cli
from qmk.comment_remover import comment_remover
default_key_entry = {'x': -1, 'y': 0, 'w': 1}
-single_comment_regex = re.compile(r' */[/*].*$')
+single_comment_regex = re.compile(r'\s+/[/*].*$')
multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE)
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py
index d07790d118..32da1a9b52 100644
--- a/lib/python/qmk/cli/__init__.py
+++ b/lib/python/qmk/cli/__init__.py
@@ -12,6 +12,56 @@ from subprocess import run
from milc import cli, __VERSION__
from milc.questions import yesno
+import_names = {
+ # A mapping of package name to importable name
+ 'pep8-naming': 'pep8ext_naming',
+ 'pyusb': 'usb.core',
+}
+
+safe_commands = [
+ # A list of subcommands we always run, even when the module imports fail
+ 'clone',
+ 'config',
+ 'env',
+ 'setup',
+]
+
+subcommands = [
+ 'qmk.cli.bux',
+ 'qmk.cli.c2json',
+ 'qmk.cli.cformat',
+ 'qmk.cli.chibios.confmigrate',
+ 'qmk.cli.clean',
+ 'qmk.cli.compile',
+ 'qmk.cli.console',
+ 'qmk.cli.docs',
+ 'qmk.cli.doctor',
+ 'qmk.cli.fileformat',
+ 'qmk.cli.flash',
+ 'qmk.cli.format.json',
+ 'qmk.cli.generate.api',
+ 'qmk.cli.generate.config_h',
+ 'qmk.cli.generate.dfu_header',
+ 'qmk.cli.generate.docs',
+ 'qmk.cli.generate.info_json',
+ 'qmk.cli.generate.keyboard_h',
+ 'qmk.cli.generate.layouts',
+ 'qmk.cli.generate.rgb_breathe_table',
+ 'qmk.cli.generate.rules_mk',
+ 'qmk.cli.hello',
+ 'qmk.cli.info',
+ 'qmk.cli.json2c',
+ 'qmk.cli.lint',
+ 'qmk.cli.list.keyboards',
+ 'qmk.cli.list.keymaps',
+ 'qmk.cli.kle2json',
+ 'qmk.cli.multibuild',
+ 'qmk.cli.new.keyboard',
+ 'qmk.cli.new.keymap',
+ 'qmk.cli.pyformat',
+ 'qmk.cli.pytest',
+]
+
def _run_cmd(*command):
"""Run a command in a subshell.
@@ -50,8 +100,8 @@ def _find_broken_requirements(requirements):
module_import = module_name.replace('-', '_')
# Not every module is importable by its own name.
- if module_name == "pep8-naming":
- module_import = "pep8ext_naming"
+ if module_name in import_names:
+ module_import = import_names[module_name]
if not find_spec(module_import):
broken_modules.append(module_name)
@@ -99,7 +149,7 @@ if sys.version_info[0] != 3 or sys.version_info[1] < 7:
milc_version = __VERSION__.split('.')
-if int(milc_version[0]) < 2 and int(milc_version[1]) < 3:
+if int(milc_version[0]) < 2 and int(milc_version[1]) < 4:
requirements = Path('requirements.txt').resolve()
print(f'Your MILC library is too old! Please upgrade: python3 -m pip install -U -r {str(requirements)}')
@@ -107,54 +157,41 @@ if int(milc_version[0]) < 2 and int(milc_version[1]) < 3:
# Check to make sure we have all our dependencies
msg_install = 'Please run `python3 -m pip install -r %s` to install required python dependencies.'
-
-if _broken_module_imports('requirements.txt'):
- if yesno('Would you like to install the required Python modules?'):
- _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt')
- else:
- print()
- print(msg_install % (str(Path('requirements.txt').resolve()),))
- print()
- exit(1)
-
-if cli.config.user.developer:
- args = sys.argv[1:]
- while args and args[0][0] == '-':
- del args[0]
- if not args or args[0] != 'config':
- if _broken_module_imports('requirements-dev.txt'):
- if yesno('Would you like to install the required developer Python modules?'):
- _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements-dev.txt')
- elif yesno('Would you like to disable developer mode?'):
- _run_cmd(sys.argv[0], 'config', 'user.developer=None')
- else:
- print()
- print(msg_install % (str(Path('requirements-dev.txt').resolve()),))
- print('You can also turn off developer mode: qmk config user.developer=None')
- print()
- exit(1)
+args = sys.argv[1:]
+while args and args[0][0] == '-':
+ del args[0]
+
+safe_command = args and args[0] in safe_commands
+
+if not safe_command:
+ if _broken_module_imports('requirements.txt'):
+ if yesno('Would you like to install the required Python modules?'):
+ _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt')
+ else:
+ print()
+ print(msg_install % (str(Path('requirements.txt').resolve()),))
+ print()
+ exit(1)
+
+ if cli.config.user.developer and _broken_module_imports('requirements-dev.txt'):
+ if yesno('Would you like to install the required developer Python modules?'):
+ _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements-dev.txt')
+ elif yesno('Would you like to disable developer mode?'):
+ _run_cmd(sys.argv[0], 'config', 'user.developer=None')
+ else:
+ print()
+ print(msg_install % (str(Path('requirements-dev.txt').resolve()),))
+ print('You can also turn off developer mode: qmk config user.developer=None')
+ print()
+ exit(1)
# Import our subcommands
-from . import bux # noqa
-from . import c2json # noqa
-from . import cformat # noqa
-from . import chibios # noqa
-from . import clean # noqa
-from . import compile # noqa
-from milc.subcommand import config # noqa
-from . import docs # noqa
-from . import doctor # noqa
-from . import fileformat # noqa
-from . import flash # noqa
-from . import format # noqa
-from . import generate # noqa
-from . import hello # noqa
-from . import info # noqa
-from . import json2c # noqa
-from . import lint # noqa
-from . import list # noqa
-from . import kle2json # noqa
-from . import multibuild # noqa
-from . import new # noqa
-from . import pyformat # noqa
-from . import pytest # noqa
+for subcommand in subcommands:
+ try:
+ __import__(subcommand)
+
+ except ModuleNotFoundError as e:
+ if safe_command:
+ print(f'Warning: Could not import {subcommand}: {e.__class__.__name__}, {e}')
+ else:
+ raise
diff --git a/lib/python/qmk/cli/chibios/__init__.py b/lib/python/qmk/cli/chibios/__init__.py
index 4301837def..e69de29bb2 100644
--- a/lib/python/qmk/cli/chibios/__init__.py
+++ b/lib/python/qmk/cli/chibios/__init__.py
@@ -1 +0,0 @@
-from . import confmigrate
diff --git a/lib/python/qmk/cli/console.py b/lib/python/qmk/cli/console.py
new file mode 100644
index 0000000000..45ff0c8bee
--- /dev/null
+++ b/lib/python/qmk/cli/console.py
@@ -0,0 +1,302 @@
+"""Acquire debugging information from usb hid devices
+
+cli implementation of https://www.pjrc.com/teensy/hid_listen.html
+"""
+from pathlib import Path
+from threading import Thread
+from time import sleep, strftime
+
+import hid
+import usb.core
+
+from milc import cli
+
+LOG_COLOR = {
+ 'next': 0,
+ 'colors': [
+ '{fg_blue}',
+ '{fg_cyan}',
+ '{fg_green}',
+ '{fg_magenta}',
+ '{fg_red}',
+ '{fg_yellow}',
+ ],
+}
+
+KNOWN_BOOTLOADERS = {
+ # VID , PID
+ ('03EB', '2FEF'): 'atmel-dfu: ATmega16U2',
+ ('03EB', '2FF0'): 'atmel-dfu: ATmega32U2',
+ ('03EB', '2FF3'): 'atmel-dfu: ATmega16U4',
+ ('03EB', '2FF4'): 'atmel-dfu: ATmega32U4',
+ ('03EB', '2FF9'): 'atmel-dfu: AT90USB64',
+ ('03EB', '2FFA'): 'atmel-dfu: AT90USB162',
+ ('03EB', '2FFB'): 'atmel-dfu: AT90USB128',
+ ('03EB', '6124'): 'Microchip SAM-BA',
+ ('0483', 'DF11'): 'stm32-dfu: STM32 BOOTLOADER',
+ ('16C0', '05DC'): 'USBasp: USBaspLoader',
+ ('16C0', '05DF'): 'bootloadHID: HIDBoot',
+ ('16C0', '0478'): 'halfkay: Teensy Halfkay',
+ ('1B4F', '9203'): 'caterina: Pro Micro 3.3V',
+ ('1B4F', '9205'): 'caterina: Pro Micro 5V',
+ ('1B4F', '9207'): 'caterina: LilyPadUSB',
+ ('1C11', 'B007'): 'kiibohd: Kiibohd DFU Bootloader',
+ ('1EAF', '0003'): 'stm32duino: Maple 003',
+ ('1FFB', '0101'): 'caterina: Polou A-Star 32U4 Bootloader',
+ ('2341', '0036'): 'caterina: Arduino Leonardo',
+ ('2341', '0037'): 'caterina: Arduino Micro',
+ ('239A', '000C'): 'caterina: Adafruit Feather 32U4',
+ ('239A', '000D'): 'caterina: Adafruit ItsyBitsy 32U4 3v',
+ ('239A', '000E'): 'caterina: Adafruit ItsyBitsy 32U4 5v',
+ ('239A', '000E'): 'caterina: Adafruit ItsyBitsy 32U4 5v',
+ ('2A03', '0036'): 'caterina: Arduino Leonardo',
+ ('2A03', '0037'): 'caterina: Arduino Micro',
+ ('314B', '0106'): 'apm32-dfu: APM32 DFU ISP Mode'
+}
+
+
+class MonitorDevice(object):
+ def __init__(self, hid_device, numeric):
+ self.hid_device = hid_device
+ self.numeric = numeric
+ self.device = hid.Device(path=hid_device['path'])
+ self.current_line = ''
+
+ cli.log.info('Console Connected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', hid_device)
+
+ def read(self, size, encoding='ascii', timeout=1):
+ """Read size bytes from the device.
+ """
+ return self.device.read(size, timeout).decode(encoding)
+
+ def read_line(self):
+ """Read from the device's console until we get a \n.
+ """
+ while '\n' not in self.current_line:
+ self.current_line += self.read(32).replace('\x00', '')
+
+ lines = self.current_line.split('\n', 1)
+ self.current_line = lines[1]
+
+ return lines[0]
+
+ def run_forever(self):
+ while True:
+ try:
+ message = {**self.hid_device, 'text': self.read_line()}
+ identifier = (int2hex(message['vendor_id']), int2hex(message['product_id'])) if self.numeric else (message['manufacturer_string'], message['product_string'])
+ message['identifier'] = ':'.join(identifier)
+ message['ts'] = '{style_dim}{fg_green}%s{style_reset_all} ' % (strftime(cli.config.general.datetime_fmt),) if cli.args.timestamp else ''
+
+ cli.echo('%(ts)s%(color)s%(identifier)s:%(index)d{style_reset_all}: %(text)s' % message)
+
+ except hid.HIDException:
+ break
+
+
+class FindDevices(object):
+ def __init__(self, vid, pid, index, numeric):
+ self.vid = vid
+ self.pid = pid
+ self.index = index
+ self.numeric = numeric
+
+ def run_forever(self):
+ """Process messages from our queue in a loop.
+ """
+ live_devices = {}
+ live_bootloaders = {}
+
+ while True:
+ try:
+ for device in list(live_devices):
+ if not live_devices[device]['thread'].is_alive():
+ cli.log.info('Console Disconnected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', live_devices[device])
+ del live_devices[device]
+
+ for device in self.find_devices():
+ if device['path'] not in live_devices:
+ device['color'] = LOG_COLOR['colors'][LOG_COLOR['next']]
+ LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
+ live_devices[device['path']] = device
+
+ try:
+ monitor = MonitorDevice(device, self.numeric)
+ device['thread'] = Thread(target=monitor.run_forever, daemon=True)
+
+ device['thread'].start()
+ except Exception as e:
+ device['e'] = e
+ device['e_name'] = e.__class__.__name__
+ cli.log.error("Could not connect to %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s:%(vendor_id)04X:%(product_id)04X:%(index)d): %(e_name)s: %(e)s", device)
+ if cli.config.general.verbose:
+ cli.log.exception(e)
+ del live_devices[device['path']]
+
+ if cli.args.bootloaders:
+ for device in self.find_bootloaders():
+ if device.address in live_bootloaders:
+ live_bootloaders[device.address]._qmk_found = True
+ else:
+ name = KNOWN_BOOTLOADERS[(int2hex(device.idVendor), int2hex(device.idProduct))]
+ cli.log.info('Bootloader Connected: {style_bright}{fg_magenta}%s', name)
+ device._qmk_found = True
+ live_bootloaders[device.address] = device
+
+ for device in list(live_bootloaders):
+ if live_bootloaders[device]._qmk_found:
+ live_bootloaders[device]._qmk_found = False
+ else:
+ name = KNOWN_BOOTLOADERS[(int2hex(live_bootloaders[device].idVendor), int2hex(live_bootloaders[device].idProduct))]
+ cli.log.info('Bootloader Disconnected: {style_bright}{fg_magenta}%s', name)
+ del live_bootloaders[device]
+
+ sleep(.1)
+
+ except KeyboardInterrupt:
+ break
+
+ def is_bootloader(self, hid_device):
+ """Returns true if the device in question matches a known bootloader vid/pid.
+ """
+ return (int2hex(hid_device.idVendor), int2hex(hid_device.idProduct)) in KNOWN_BOOTLOADERS
+
+ def is_console_hid(self, hid_device):
+ """Returns true when the usage page indicates it's a teensy-style console.
+ """
+ return hid_device['usage_page'] == 0xFF31 and hid_device['usage'] == 0x0074
+
+ def is_filtered_device(self, hid_device):
+ """Returns True if the device should be included in the list of available consoles.
+ """
+ return int2hex(hid_device['vendor_id']) == self.vid and int2hex(hid_device['product_id']) == self.pid
+
+ def find_devices_by_report(self, hid_devices):
+ """Returns a list of available teensy-style consoles by doing a brute-force search.
+
+ Some versions of linux don't report usage and usage_page. In that case we fallback to reading the report (possibly inaccurately) ourselves.
+ """
+ devices = []
+
+ for device in hid_devices:
+ path = device['path'].decode('utf-8')
+
+ if path.startswith('/dev/hidraw'):
+ number = path[11:]
+ report = Path(f'/sys/class/hidraw/hidraw{number}/device/report_descriptor')
+
+ if report.exists():
+ rp = report.read_bytes()
+
+ if rp[1] == 0x31 and rp[3] == 0x09:
+ devices.append(device)
+
+ return devices
+
+ def find_bootloaders(self):
+ """Returns a list of available bootloader devices.
+ """
+ return list(filter(self.is_bootloader, usb.core.find(find_all=True)))
+
+ def find_devices(self):
+ """Returns a list of available teensy-style consoles.
+ """
+ hid_devices = hid.enumerate()
+ devices = list(filter(self.is_console_hid, hid_devices))
+
+ if not devices:
+ devices = self.find_devices_by_report(hid_devices)
+
+ if self.vid and self.pid:
+ devices = list(filter(self.is_filtered_device, devices))
+
+ # Add index numbers
+ device_index = {}
+ for device in devices:
+ id = ':'.join((int2hex(device['vendor_id']), int2hex(device['product_id'])))
+
+ if id not in device_index:
+ device_index[id] = 0
+
+ device_index[id] += 1
+ device['index'] = device_index[id]
+
+ return devices
+
+
+def int2hex(number):
+ """Returns a string representation of the number as hex.
+ """
+ return "%04X" % number
+
+
+def list_devices(device_finder):
+ """Show the user a nicely formatted list of devices.
+ """
+ devices = device_finder.find_devices()
+
+ if devices:
+ cli.log.info('Available devices:')
+ for dev in devices:
+ color = LOG_COLOR['colors'][LOG_COLOR['next']]
+ LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
+ cli.log.info("\t%s%s:%s:%d{style_reset_all}\t%s %s", color, int2hex(dev['vendor_id']), int2hex(dev['product_id']), dev['index'], dev['manufacturer_string'], dev['product_string'])
+
+ if cli.args.bootloaders:
+ bootloaders = device_finder.find_bootloaders()
+
+ if bootloaders:
+ cli.log.info('Available Bootloaders:')
+
+ for dev in bootloaders:
+ cli.log.info("\t%s:%s\t%s", int2hex(dev.idVendor), int2hex(dev.idProduct), KNOWN_BOOTLOADERS[(int2hex(dev.idVendor), int2hex(dev.idProduct))])
+
+
+@cli.argument('--bootloaders', arg_only=True, default=True, action='store_boolean', help='displaying bootloaders.')
+@cli.argument('-d', '--device', help='Device to select - uses format <pid>:<vid>[:<index>].')
+@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available hid_listen devices.')
+@cli.argument('-n', '--numeric', arg_only=True, action='store_true', help='Show VID/PID instead of names.')
+@cli.argument('-t', '--timestamp', arg_only=True, action='store_true', help='Print the timestamp for received messages as well.')
+@cli.argument('-w', '--wait', type=int, default=1, help="How many seconds to wait between checks (Default: 1)")
+@cli.subcommand('Acquire debugging information from usb hid devices.', hidden=False if cli.config.user.developer else True)
+def console(cli):
+ """Acquire debugging information from usb hid devices
+ """
+ vid = None
+ pid = None
+ index = 1
+
+ if cli.config.console.device:
+ device = cli.config.console.device.split(':')
+
+ if len(device) == 2:
+ vid, pid = device
+
+ elif len(device) == 3:
+ vid, pid, index = device
+
+ if not index.isdigit():
+ cli.log.error('Device index must be a number! Got "%s" instead.', index)
+ exit(1)
+
+ index = int(index)
+
+ if index < 1:
+ cli.log.error('Device index must be greater than 0! Got %s', index)
+ exit(1)
+
+ else:
+ cli.log.error('Invalid format for device, expected "<pid>:<vid>[:<index>]" but got "%s".', cli.config.console.device)
+ cli.print_help()
+ exit(1)
+
+ vid = vid.upper()
+ pid = pid.upper()
+
+ device_finder = FindDevices(vid, pid, index, cli.args.numeric)
+
+ if cli.args.list:
+ return list_devices(device_finder)
+
+ print('Looking for devices...', flush=True)
+ device_finder.run_forever()
diff --git a/lib/python/qmk/cli/format/__init__.py b/lib/python/qmk/cli/format/__init__.py
index 741ec778b1..e69de29bb2 100644
--- a/lib/python/qmk/cli/format/__init__.py
+++ b/lib/python/qmk/cli/format/__init__.py
@@ -1 +0,0 @@
-from . import json
diff --git a/lib/python/qmk/cli/generate/__init__.py b/lib/python/qmk/cli/generate/__init__.py
index 0efca0022d..e69de29bb2 100644
--- a/lib/python/qmk/cli/generate/__init__.py
+++ b/lib/python/qmk/cli/generate/__init__.py
@@ -1,9 +0,0 @@
-from . import api
-from . import config_h
-from . import dfu_header
-from . import docs
-from . import info_json
-from . import keyboard_h
-from . import layouts
-from . import rgb_breathe_table
-from . import rules_mk
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'
diff --git a/lib/python/qmk/cli/list/__init__.py b/lib/python/qmk/cli/list/__init__.py
index d83cd20b5b..e69de29bb2 100644
--- a/lib/python/qmk/cli/list/__init__.py
+++ b/lib/python/qmk/cli/list/__init__.py
@@ -1,2 +0,0 @@
-from . import keyboards
-from . import keymaps
diff --git a/lib/python/qmk/cli/multibuild.py b/lib/python/qmk/cli/multibuild.py
index 46594c0997..bdb0b493c8 100755
--- a/lib/python/qmk/cli/multibuild.py
+++ b/lib/python/qmk/cli/multibuild.py
@@ -2,6 +2,7 @@
This will compile everything in parallel, for testing purposes.
"""
+import os
import re
from pathlib import Path
from subprocess import DEVNULL
@@ -11,6 +12,7 @@ from milc import cli
from qmk.constants import QMK_FIRMWARE
from qmk.commands import _find_make
import qmk.keyboard
+import qmk.keymap
def _make_rules_mk_filter(key, value):
@@ -29,6 +31,7 @@ def _is_split(keyboard_name):
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.")
@cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.")
@cli.argument('-f', '--filter', arg_only=True, action='append', default=[], help="Filter the list of keyboards based on the supplied value in rules.mk. Supported format is 'SPLIT_KEYBOARD=yes'. May be passed multiple times.")
+@cli.argument('-km', '--keymap', type=str, default='default', help="The keymap name to build. Default is 'default'.")
@cli.subcommand('Compile QMK Firmware for all keyboards.', hidden=False if cli.config.user.developer else True)
def multibuild(cli):
"""Compile QMK Firmware against all keyboards.
@@ -57,23 +60,29 @@ def multibuild(cli):
builddir.mkdir(parents=True, exist_ok=True)
with open(makefile, "w") as f:
for keyboard_name in keyboard_list:
- keyboard_safe = keyboard_name.replace('/', '_')
- # yapf: disable
- f.write(
- f"""\
+ if qmk.keymap.locate_keymap(keyboard_name, cli.args.keymap) is not None:
+ keyboard_safe = keyboard_name.replace('/', '_')
+ # yapf: disable
+ f.write(
+ f"""\
all: {keyboard_safe}_binary
{keyboard_safe}_binary:
@rm -f "{QMK_FIRMWARE}/.build/failed.log.{keyboard_safe}" || true
- +@$(MAKE) -C "{QMK_FIRMWARE}" -f "{QMK_FIRMWARE}/build_keyboard.mk" KEYBOARD="{keyboard_name}" KEYMAP="default" REQUIRE_PLATFORM_KEY= COLOR=true SILENT=false \\
- >>"{QMK_FIRMWARE}/.build/build.log.{keyboard_safe}" 2>&1 \\
- || cp "{QMK_FIRMWARE}/.build/build.log.{keyboard_safe}" "{QMK_FIRMWARE}/.build/failed.log.{keyboard_safe}"
- @{{ grep '\[ERRORS\]' "{QMK_FIRMWARE}/.build/build.log.{keyboard_safe}" >/dev/null 2>&1 && printf "Build %-64s \e[1;31m[ERRORS]\e[0m\\n" "{keyboard_name}:default" ; }} \\
- || {{ grep '\[WARNINGS\]' "{QMK_FIRMWARE}/.build/build.log.{keyboard_safe}" >/dev/null 2>&1 && printf "Build %-64s \e[1;33m[WARNINGS]\e[0m\\n" "{keyboard_name}:default" ; }} \\
- || printf "Build %-64s \e[1;32m[OK]\e[0m\\n" "{keyboard_name}:default"
- @rm -f "{QMK_FIRMWARE}/.build/build.log.{keyboard_safe}" || true
+ +@$(MAKE) -C "{QMK_FIRMWARE}" -f "{QMK_FIRMWARE}/build_keyboard.mk" KEYBOARD="{keyboard_name}" KEYMAP="{cli.args.keymap}" REQUIRE_PLATFORM_KEY= COLOR=true SILENT=false \\
+ >>"{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}" 2>&1 \\
+ || cp "{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}" "{QMK_FIRMWARE}/.build/failed.log.{os.getpid()}.{keyboard_safe}"
+ @{{ grep '\[ERRORS\]' "{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}" >/dev/null 2>&1 && printf "Build %-64s \e[1;31m[ERRORS]\e[0m\\n" "{keyboard_name}:{cli.args.keymap}" ; }} \\
+ || {{ grep '\[WARNINGS\]' "{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}" >/dev/null 2>&1 && printf "Build %-64s \e[1;33m[WARNINGS]\e[0m\\n" "{keyboard_name}:{cli.args.keymap}" ; }} \\
+ || printf "Build %-64s \e[1;32m[OK]\e[0m\\n" "{keyboard_name}:{cli.args.keymap}"
+ @rm -f "{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}" || true
"""# noqa
- )
- # yapf: enable
+ )
+ # yapf: enable
- cli.run([make_cmd, '-j', str(cli.args.parallel), '-f', makefile, 'all'], capture_output=False, stdin=DEVNULL)
+ cli.run([make_cmd, '-j', str(cli.args.parallel), '-f', makefile.as_posix(), 'all'], capture_output=False, stdin=DEVNULL)
+
+ # Check for failures
+ failures = [f for f in builddir.glob(f'failed.log.{os.getpid()}.*')]
+ if len(failures) > 0:
+ return False
diff --git a/lib/python/qmk/cli/new/__init__.py b/lib/python/qmk/cli/new/__init__.py
index fe5d6fe483..e69de29bb2 100644
--- a/lib/python/qmk/cli/new/__init__.py
+++ b/lib/python/qmk/cli/new/__init__.py
@@ -1,2 +0,0 @@
-from . import keyboard
-from . import keymap
diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py
index ee049e8af7..3a35c11031 100644
--- a/lib/python/qmk/commands.py
+++ b/lib/python/qmk/commands.py
@@ -201,6 +201,7 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va
f'VERBOSE={verbose}',
f'COLOR={color}',
'SILENT=false',
+ f'QMK_BIN={"bin/qmk" if "DEPRECATED_BIN_QMK" in os.environ else "qmk"}',
])
return make_command
diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py
index 3ed69f3bf9..49e5e0eb42 100644
--- a/lib/python/qmk/constants.py
+++ b/lib/python/qmk/constants.py
@@ -10,7 +10,7 @@ QMK_FIRMWARE = Path.cwd()
MAX_KEYBOARD_SUBFOLDERS = 5
# Supported processor types
-CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F411', 'STM32G431', 'STM32G474'
+CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK66F18', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32L433', 'STM32L443'
LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None
VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85'
diff --git a/lib/python/qmk/decorators.py b/lib/python/qmk/decorators.py
index 8d43ae980f..f6270990b9 100644
--- a/lib/python/qmk/decorators.py
+++ b/lib/python/qmk/decorators.py
@@ -17,12 +17,12 @@ def automagic_keyboard(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Ensure that `--keyboard` was not passed and CWD is under `qmk_firmware/keyboards`
- if cli.config_source[cli._entrypoint.__name__]['keyboard'] != 'argument':
+ if cli.config_source[cli._subcommand.__name__]['keyboard'] != 'argument':
keyboard = find_keyboard_from_dir()
if keyboard:
- cli.config[cli._entrypoint.__name__]['keyboard'] = keyboard
- cli.config_source[cli._entrypoint.__name__]['keyboard'] = 'keyboard_directory'
+ cli.config[cli._subcommand.__name__]['keyboard'] = keyboard
+ cli.config_source[cli._subcommand.__name__]['keyboard'] = 'keyboard_directory'
return func(*args, **kwargs)
@@ -37,12 +37,12 @@ def automagic_keymap(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Ensure that `--keymap` was not passed and that we're under `qmk_firmware`
- if cli.config_source[cli._entrypoint.__name__]['keymap'] != 'argument':
+ if cli.config_source[cli._subcommand.__name__]['keymap'] != 'argument':
keymap_name, keymap_type = find_keymap_from_dir()
if keymap_name:
- cli.config[cli._entrypoint.__name__]['keymap'] = keymap_name
- cli.config_source[cli._entrypoint.__name__]['keymap'] = keymap_type
+ cli.config[cli._subcommand.__name__]['keymap'] = keymap_name
+ cli.config_source[cli._subcommand.__name__]['keymap'] = keymap_type
return func(*args, **kwargs)
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py
index a7b70a7d99..afdbc81429 100644
--- a/lib/python/qmk/tests/test_cli_commands.py
+++ b/lib/python/qmk/tests/test_cli_commands.py
@@ -7,7 +7,7 @@ is_windows = 'windows' in platform.platform().lower()
def check_subcommand(command, *args):
- cmd = ['bin/qmk', command, *args]
+ cmd = ['qmk', command, *args]
result = cli.run(cmd, stdin=DEVNULL, combined_output=True)
return result
@@ -16,7 +16,7 @@ def check_subcommand_stdin(file_to_read, command, *args):
"""Pipe content of a file to a command and return output.
"""
with open(file_to_read, encoding='utf-8') as my_file:
- cmd = ['bin/qmk', command, *args]
+ cmd = ['qmk', command, *args]
result = cli.run(cmd, stdin=my_file, combined_output=True)
return result