#!/usr/bin/env python3 """CLI wrapper for running QMK commands. This program can be run from anywhere, with or without a qmk_firmware repository. If a qmk_firmware repository can be located we will use that to augment our available subcommands. """ import os from pathlib import Path import shlex import subprocess import sys from platform import platform from traceback import print_exc import milc import platformdirs from . import __version__ from .helpers import find_qmk_firmware, is_qmk_firmware, find_qmk_userspace, is_qmk_userspace def _get_default_distrib_path(): if 'windows' in platform().lower(): try: result = milc.cli.run(['cygpath', '-w', '/opt/qmk']) if result.returncode == 0: return result.stdout.strip() except Exception: pass return platformdirs.user_data_dir('qmk') # Ensure the QMK distribution is on the `$PATH` if present. This must be kept in sync with qmk/qmk_firmware. QMK_DISTRIB_DIR = Path(os.environ['QMK_DISTRIB_DIR'] if 'QMK_DISTRIB_DIR' in os.environ else _get_default_distrib_path()) if QMK_DISTRIB_DIR.exists(): os.environ['PATH'] = str(QMK_DISTRIB_DIR / 'bin') + os.pathsep + os.environ['PATH'] # Prepend any user-defined path prefix if 'QMK_PATH_PREFIX' in os.environ: os.environ['PATH'] = os.environ['QMK_PATH_PREFIX'] + os.pathsep + os.environ['PATH'] milc.cli.milc_options(version=__version__) milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}' # These must happen after the milc.milc_options() call import milc.subcommand.config # noqa: E402, must come after milc.milc_options() @milc.cli.entrypoint('CLI wrapper for running QMK commands.') def qmk_main(cli): """The function that gets run when there's no subcommand. """ cli.print_help() def run_cmd(*command): """Run a command in a subshell. """ if 'windows' in milc.cli.platform.lower(): safecmd = map(shlex.quote, command) safecmd = ' '.join(safecmd) command = [os.environ['SHELL'], '-c', safecmd] return subprocess.run(command) # Python setuptools entrypoint def main(): """Setup the environment before dispatching to the entrypoint. """ # Warn if they use an outdated python version if sys.version_info < (3, 9): print('Warning: Your Python version is out of date! Some subcommands may not work!') print('Please upgrade to Python 3.9 or later.') # Environment setup qmk_userspace = find_qmk_userspace() qmk_firmware = find_qmk_firmware() if is_qmk_userspace(qmk_userspace): os.environ['QMK_USERSPACE'] = str(qmk_userspace) elif 'QMK_USERSPACE' in os.environ: # Failed to find valid userspace, including what was in the environment if specified -- wipe any environment variable if present as it's clearly invalid. os.environ.pop('QMK_USERSPACE') os.environ['QMK_HOME'] = str(qmk_firmware) os.environ['ORIG_CWD'] = os.getcwd() import qmk_cli.subcommands # noqa: F401 # Check out and initialize the qmk_firmware environment if is_qmk_firmware(qmk_firmware): # All subcommands are run relative to the qmk_firmware root to make it easier to use the right copy of qmk_firmware. os.chdir(str(qmk_firmware)) sys.path.append(str(qmk_firmware / 'lib/python')) try: import qmk.cli # noqa: F401 except ImportError as e: if qmk_firmware.name != 'qmk_firmware': print('Warning: %s does not end in "qmk_firmware". Do you need to set QMK_HOME to "%s/qmk_firmware"?' % (qmk_firmware, qmk_firmware)) print('Error: %s: %s', (e.__class__.__name__, e)) print_exc() sys.exit(1) # Call the entrypoint return_code = milc.cli() if return_code is False: exit(1) elif return_code is not True and isinstance(return_code, int): if return_code < 0 or return_code > 255: milc.cli.log.error('Invalid return_code: %d', return_code) exit(255) exit(return_code) exit(0)