flash.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. """Compile and flash QMK Firmware
  2. You can compile a keymap already in the repo or using a QMK Configurator export.
  3. A bootloader must be specified.
  4. """
  5. from argcomplete.completers import FilesCompleter
  6. from milc import cli
  7. import qmk.path
  8. from qmk.decorators import automagic_keyboard, automagic_keymap
  9. from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json, build_environment
  10. from qmk.keyboard import keyboard_completer, keyboard_folder
  11. from qmk.keymap import keymap_completer, locate_keymap
  12. from qmk.flashers import flasher
  13. def _is_keymap_target(keyboard, keymap):
  14. if keymap == 'all':
  15. return True
  16. if locate_keymap(keyboard, keymap):
  17. return True
  18. return False
  19. def _list_bootloaders():
  20. """Prints the available bootloaders listed in docs.qmk.fm.
  21. """
  22. cli.print_help()
  23. cli.log.info('Here are the available bootloaders:')
  24. cli.echo('\tavrdude')
  25. cli.echo('\tbootloadhid')
  26. cli.echo('\tdfu')
  27. cli.echo('\tdfu-util')
  28. cli.echo('\tmdloader')
  29. cli.echo('\tst-flash')
  30. cli.echo('\tst-link-cli')
  31. cli.log.info('Enhanced variants for split keyboards:')
  32. cli.echo('\tavrdude-split-left')
  33. cli.echo('\tavrdude-split-right')
  34. cli.echo('\tdfu-ee')
  35. cli.echo('\tdfu-split-left')
  36. cli.echo('\tdfu-split-right')
  37. cli.echo('\tdfu-util-split-left')
  38. cli.echo('\tdfu-util-split-right')
  39. cli.echo('\tuf2-split-left')
  40. cli.echo('\tuf2-split-right')
  41. cli.echo('For more info, visit https://docs.qmk.fm/#/flashing')
  42. return False
  43. def _flash_binary(filename, mcu):
  44. """Try to flash binary firmware
  45. """
  46. cli.echo('Flashing binary firmware...\nPlease reset your keyboard into bootloader mode now!\nPress Ctrl-C to exit.\n')
  47. try:
  48. err, msg = flasher(mcu, filename)
  49. if err:
  50. cli.log.error(msg)
  51. return False
  52. except KeyboardInterrupt:
  53. cli.log.info('Ctrl-C was pressed, exiting...')
  54. return True
  55. @cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.')
  56. @cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.')
  57. @cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.')
  58. @cli.argument('-m', '--mcu', help='The MCU name. Required for HalfKay, HID, USBAspLoader and ISP flashing.')
  59. @cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
  60. @cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
  61. @cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
  62. @cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.")
  63. @cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")
  64. @cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.")
  65. @cli.subcommand('QMK Flash.')
  66. @automagic_keyboard
  67. @automagic_keymap
  68. def flash(cli):
  69. """Compile and or flash QMK Firmware or keyboard/layout
  70. If a binary firmware is supplied, try to flash that.
  71. If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists.
  72. If a keyboard and keymap are provided this command will build a firmware based on that.
  73. If bootloader is omitted the make system will use the configured bootloader for that keyboard.
  74. """
  75. if cli.args.filename and cli.args.filename.suffix in ['.bin', '.hex', '.uf2']:
  76. return _flash_binary(cli.args.filename, cli.args.mcu)
  77. if cli.args.bootloaders:
  78. return _list_bootloaders()
  79. # Build the environment vars
  80. envs = build_environment(cli.args.env)
  81. # Determine the compile command
  82. commands = []
  83. if cli.args.filename:
  84. # If a configurator JSON was provided generate a keymap and compile it
  85. user_keymap = parse_configurator_json(cli.args.filename)
  86. commands = [compile_configurator_json(user_keymap, cli.args.bootloader, parallel=cli.config.flash.parallel, clean=cli.args.clean, **envs)]
  87. elif cli.config.flash.keyboard and cli.config.flash.keymap:
  88. # Generate the make command for a specific keyboard/keymap.
  89. if not _is_keymap_target(cli.config.flash.keyboard, cli.config.flash.keymap):
  90. cli.log.error('Invalid keymap argument.')
  91. cli.print_help()
  92. return False
  93. if cli.args.clean:
  94. commands.append(create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, 'clean', **envs))
  95. commands.append(create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, cli.args.bootloader, parallel=cli.config.flash.parallel, **envs))
  96. if not commands:
  97. cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
  98. cli.print_help()
  99. return False
  100. cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(commands[-1]))
  101. if not cli.args.dry_run:
  102. cli.echo('\n')
  103. for command in commands:
  104. ret = cli.run(command, capture_output=False)
  105. if ret.returncode:
  106. return ret.returncode