doctor.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. """QMK Python Doctor
  2. Check up for QMK environment.
  3. """
  4. import os
  5. import platform
  6. import shutil
  7. import subprocess
  8. import glob
  9. from milc import cli
  10. def _udev_rule(vid, pid = None):
  11. """ Helper function that return udev rules
  12. """
  13. if pid:
  14. return 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="%s", ATTRS{idProduct}=="%s", MODE:="0666"' % (vid, pid)
  15. else:
  16. return 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="%s", MODE:="0666"' % vid
  17. @cli.subcommand('Basic QMK environment checks')
  18. def doctor(cli):
  19. """Basic QMK environment checks.
  20. This is currently very simple, it just checks that all the expected binaries are on your system.
  21. TODO(unclaimed):
  22. * [ ] Compile a trivial program with each compiler
  23. """
  24. cli.log.info('QMK Doctor is checking your environment.')
  25. # Make sure the basic CLI tools we need are available and can be executed.
  26. binaries = ['dfu-programmer', 'avrdude', 'dfu-util', 'avr-gcc', 'arm-none-eabi-gcc', 'bin/qmk']
  27. ok = True
  28. for binary in binaries:
  29. res = shutil.which(binary)
  30. if res is None:
  31. cli.log.error("{fg_red}QMK can't find %s in your path.", binary)
  32. ok = False
  33. else:
  34. check = subprocess.run([binary, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
  35. if check.returncode in [0, 1]:
  36. cli.log.info('Found {fg_cyan}%s', binary)
  37. else:
  38. cli.log.error("{fg_red}Can't run `%s --version`", binary)
  39. ok = False
  40. # Determine our OS and run platform specific tests
  41. OS = platform.system() # noqa (N806), uppercase name is ok in this instance
  42. if OS == "Darwin":
  43. cli.log.info("Detected {fg_cyan}macOS.")
  44. elif OS == "Linux":
  45. cli.log.info("Detected {fg_cyan}Linux.")
  46. # Checking for udev rules
  47. udev_dir = "/etc/udev/rules.d/"
  48. # These are the recommended udev rules
  49. desired_rules = dict(dfu = {_udev_rule("03eb", "2ff4"),_udev_rule("03eb", "2ffb"), _udev_rule("03eb", "2ff0")},
  50. tmk = {_udev_rule("feed")},
  51. input_club = {_udev_rule("1c11")},
  52. stm32 = {_udev_rule("1eaf", "0003"),_udev_rule("0483", "df11")},
  53. caterina = {'ATTRS{idVendor}=="2a03", ENV{ID_MM_DEVICE_IGNORE}="1"',
  54. 'ATTRS{idVendor}=="2341", ENV{ID_MM_DEVICE_IGNORE}="1"'}
  55. )
  56. if os.path.exists(udev_dir):
  57. udev_rules = [rule for rule in glob.iglob(os.path.join(udev_dir, "*.rules")) if os.path.isfile(rule)]
  58. # Collect all rules from the config files
  59. current_rules = set()
  60. for rule in udev_rules:
  61. with open(rule, "r") as fd:
  62. for line in fd.readlines():
  63. line = line.strip()
  64. if not line.startswith("#") and len(line):
  65. current_rules.add(line)
  66. # Check if the desired rules are among the currently present rules
  67. for bootloader, rules in desired_rules.items():
  68. if not rules.issubset(current_rules):
  69. # If the rules for catalina are not present, check if ModemManager is running
  70. if bootloader == "caterina":
  71. if shutil.which("systemctl"):
  72. mm_check = subprocess.run(["systemctl", "--quiet", "is-active", "ModemManager.service"], timeout=10)
  73. if mm_check.returncode == 0:
  74. ok = False
  75. cli.log.warn("{bg_yellow}Detected ModemManager without udev rules. Please either disable it or set the appropriate udev rules if you are using a Pro Micro.")
  76. else:
  77. cli.log.warn("Can't find systemctl to check for ModemManager.")
  78. else:
  79. cli.log.warn("{bg_yellow}Missing udev rules for '%s' boards. You'll need to use `sudo` in order to flash them.", bootloader)
  80. else:
  81. cli.log.info("Assuming {fg_cyan}Windows.")
  82. # Report a summary of our findings to the user
  83. if ok:
  84. cli.log.info('{fg_green}QMK is ready to go')
  85. else:
  86. cli.log.info('{fg_yellow}Problems detected, please fix these problems before proceeding.')
  87. # FIXME(skullydazed): Link to a document about troubleshooting, or discord or something