You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
4.7 KiB

  1. """Functions that help us work with files and folders.
  2. """
  3. import logging
  4. import os
  5. import argparse
  6. from pathlib import Path
  7. from qmk.constants import MAX_KEYBOARD_SUBFOLDERS, QMK_FIRMWARE, QMK_USERSPACE, HAS_QMK_USERSPACE
  8. from qmk.errors import NoSuchKeyboardError
  9. def is_keyboard(keyboard_name):
  10. """Returns True if `keyboard_name` is a keyboard we can compile.
  11. """
  12. if keyboard_name:
  13. keyboard_path = QMK_FIRMWARE / 'keyboards' / keyboard_name
  14. rules_mk = keyboard_path / 'rules.mk'
  15. return rules_mk.exists()
  16. def under_qmk_firmware(path=Path(os.environ['ORIG_CWD'])):
  17. """Returns a Path object representing the relative path under qmk_firmware, or None.
  18. """
  19. try:
  20. return path.relative_to(QMK_FIRMWARE)
  21. except ValueError:
  22. return None
  23. def under_qmk_userspace(path=Path(os.environ['ORIG_CWD'])):
  24. """Returns a Path object representing the relative path under $QMK_USERSPACE, or None.
  25. """
  26. try:
  27. if HAS_QMK_USERSPACE:
  28. return path.relative_to(QMK_USERSPACE)
  29. except ValueError:
  30. pass
  31. return None
  32. def is_under_qmk_firmware(path=Path(os.environ['ORIG_CWD'])):
  33. """Returns a boolean if the input path is a child under qmk_firmware.
  34. """
  35. if path is None:
  36. return False
  37. try:
  38. return Path(os.path.commonpath([Path(path), QMK_FIRMWARE])) == QMK_FIRMWARE
  39. except ValueError:
  40. return False
  41. def is_under_qmk_userspace(path=Path(os.environ['ORIG_CWD'])):
  42. """Returns a boolean if the input path is a child under $QMK_USERSPACE.
  43. """
  44. if path is None:
  45. return False
  46. try:
  47. if HAS_QMK_USERSPACE:
  48. return Path(os.path.commonpath([Path(path), QMK_USERSPACE])) == QMK_USERSPACE
  49. except ValueError:
  50. return False
  51. def keyboard(keyboard_name):
  52. """Returns the path to a keyboard's directory relative to the qmk root.
  53. """
  54. return Path('keyboards') / keyboard_name
  55. def keymaps(keyboard_name):
  56. """Returns all of the `keymaps/` directories for a given keyboard.
  57. Args:
  58. keyboard_name
  59. The name of the keyboard. Example: clueboard/66/rev3
  60. """
  61. keyboard_folder = keyboard(keyboard_name)
  62. found_dirs = []
  63. if HAS_QMK_USERSPACE:
  64. this_keyboard_folder = Path(QMK_USERSPACE) / keyboard_folder
  65. for _ in range(MAX_KEYBOARD_SUBFOLDERS):
  66. if (this_keyboard_folder / 'keymaps').exists():
  67. found_dirs.append((this_keyboard_folder / 'keymaps').resolve())
  68. this_keyboard_folder = this_keyboard_folder.parent
  69. if this_keyboard_folder.resolve() == QMK_USERSPACE.resolve():
  70. break
  71. # We don't have any relevant keymap directories in userspace, so we'll use the fully-qualified path instead.
  72. if len(found_dirs) == 0:
  73. found_dirs.append((QMK_USERSPACE / keyboard_folder / 'keymaps').resolve())
  74. this_keyboard_folder = QMK_FIRMWARE / keyboard_folder
  75. for _ in range(MAX_KEYBOARD_SUBFOLDERS):
  76. if (this_keyboard_folder / 'keymaps').exists():
  77. found_dirs.append((this_keyboard_folder / 'keymaps').resolve())
  78. this_keyboard_folder = this_keyboard_folder.parent
  79. if this_keyboard_folder.resolve() == QMK_FIRMWARE.resolve():
  80. break
  81. if len(found_dirs) > 0:
  82. return found_dirs
  83. logging.error('Could not find the keymaps directory!')
  84. raise NoSuchKeyboardError('Could not find keymaps directory for: %s' % keyboard_name)
  85. def keymap(keyboard_name, keymap_name):
  86. """Locate the directory of a given keymap.
  87. Args:
  88. keyboard_name
  89. The name of the keyboard. Example: clueboard/66/rev3
  90. keymap_name
  91. The name of the keymap. Example: default
  92. """
  93. for keymap_dir in keymaps(keyboard_name):
  94. if (keymap_dir / keymap_name).exists():
  95. return (keymap_dir / keymap_name).resolve()
  96. def normpath(path):
  97. """Returns a `pathlib.Path()` object for a given path.
  98. This will use the path to a file as seen from the directory the script was called from. You should use this to normalize filenames supplied from the command line.
  99. """
  100. path = Path(path)
  101. if path.is_absolute():
  102. return path
  103. return Path(os.environ['ORIG_CWD']) / path
  104. class FileType(argparse.FileType):
  105. def __init__(self, *args, **kwargs):
  106. # Use UTF8 by default for stdin
  107. if 'encoding' not in kwargs:
  108. kwargs['encoding'] = 'UTF-8'
  109. return super().__init__(*args, **kwargs)
  110. def __call__(self, string):
  111. """normalize and check exists
  112. otherwise magic strings like '-' for stdin resolve to bad paths
  113. """
  114. norm = normpath(string)
  115. return norm if norm.exists() else super().__call__(string)