commands.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. """Helper functions for commands.
  2. """
  3. import json
  4. import os
  5. import shutil
  6. from pathlib import Path
  7. from subprocess import DEVNULL
  8. from time import strftime
  9. from milc import cli
  10. import qmk.keymap
  11. from qmk.constants import KEYBOARD_OUTPUT_PREFIX
  12. from qmk.json_schema import json_load
  13. time_fmt = '%Y-%m-%d-%H:%M:%S'
  14. def _find_make():
  15. """Returns the correct make command for this environment.
  16. """
  17. make_cmd = os.environ.get('MAKE')
  18. if not make_cmd:
  19. make_cmd = 'gmake' if shutil.which('gmake') else 'make'
  20. return make_cmd
  21. def create_make_target(target, parallel=1, **env_vars):
  22. """Create a make command
  23. Args:
  24. target
  25. Usually a make rule, such as 'clean' or 'all'.
  26. parallel
  27. The number of make jobs to run in parallel
  28. **env_vars
  29. Environment variables to be passed to make.
  30. Returns:
  31. A command that can be run to make the specified keyboard and keymap
  32. """
  33. env = []
  34. make_cmd = _find_make()
  35. for key, value in env_vars.items():
  36. env.append(f'{key}={value}')
  37. return [make_cmd, '-j', str(parallel), *env, target]
  38. def create_make_command(keyboard, keymap, target=None, parallel=1, **env_vars):
  39. """Create a make compile command
  40. Args:
  41. keyboard
  42. The path of the keyboard, for example 'plank'
  43. keymap
  44. The name of the keymap, for example 'algernon'
  45. target
  46. Usually a bootloader.
  47. parallel
  48. The number of make jobs to run in parallel
  49. **env_vars
  50. Environment variables to be passed to make.
  51. Returns:
  52. A command that can be run to make the specified keyboard and keymap
  53. """
  54. make_args = [keyboard, keymap]
  55. if target:
  56. make_args.append(target)
  57. return create_make_target(':'.join(make_args), parallel, **env_vars)
  58. def get_git_version(current_time, repo_dir='.', check_dir='.'):
  59. """Returns the current git version for a repo, or the current time.
  60. """
  61. git_describe_cmd = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags']
  62. if repo_dir != '.':
  63. repo_dir = Path('lib') / repo_dir
  64. if check_dir != '.':
  65. check_dir = repo_dir / check_dir
  66. if Path(check_dir).exists():
  67. git_describe = cli.run(git_describe_cmd, stdin=DEVNULL, cwd=repo_dir)
  68. if git_describe.returncode == 0:
  69. return git_describe.stdout.strip()
  70. else:
  71. cli.log.warn(f'"{" ".join(git_describe_cmd)}" returned error code {git_describe.returncode}')
  72. print(git_describe.stderr)
  73. return current_time
  74. return current_time
  75. def create_version_h(skip_git=False, skip_all=False):
  76. """Generate version.h contents
  77. """
  78. if skip_all:
  79. current_time = "1970-01-01-00:00:00"
  80. else:
  81. current_time = strftime(time_fmt)
  82. if skip_git:
  83. git_version = "NA"
  84. chibios_version = "NA"
  85. chibios_contrib_version = "NA"
  86. else:
  87. git_version = get_git_version(current_time)
  88. chibios_version = get_git_version(current_time, "chibios", "os")
  89. chibios_contrib_version = get_git_version(current_time, "chibios-contrib", "os")
  90. version_h_lines = f"""/* This file was automatically generated. Do not edit or copy.
  91. */
  92. #pragma once
  93. #define QMK_VERSION "{git_version}"
  94. #define QMK_BUILDDATE "{current_time}"
  95. #define CHIBIOS_VERSION "{chibios_version}"
  96. #define CHIBIOS_CONTRIB_VERSION "{chibios_contrib_version}"
  97. """
  98. return version_h_lines
  99. def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_vars):
  100. """Convert a configurator export JSON file into a C file and then compile it.
  101. Args:
  102. user_keymap
  103. A deserialized keymap export
  104. bootloader
  105. A bootloader to flash
  106. parallel
  107. The number of make jobs to run in parallel
  108. Returns:
  109. A command to run to compile and flash the C file.
  110. """
  111. # Write the keymap.c file
  112. keyboard_filesafe = user_keymap['keyboard'].replace('/', '_')
  113. target = f'{keyboard_filesafe}_{user_keymap["keymap"]}'
  114. keyboard_output = Path(f'{KEYBOARD_OUTPUT_PREFIX}{keyboard_filesafe}')
  115. keymap_output = Path(f'{keyboard_output}_{user_keymap["keymap"]}')
  116. c_text = qmk.keymap.generate_c(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
  117. keymap_dir = keymap_output / 'src'
  118. keymap_c = keymap_dir / 'keymap.c'
  119. keymap_dir.mkdir(exist_ok=True, parents=True)
  120. keymap_c.write_text(c_text)
  121. version_h = Path('quantum/version.h')
  122. version_h.write_text(create_version_h())
  123. # Return a command that can be run to make the keymap and flash if given
  124. verbose = 'true' if cli.config.general.verbose else 'false'
  125. color = 'true' if cli.config.general.color else 'false'
  126. make_command = [_find_make()]
  127. if not cli.config.general.verbose:
  128. make_command.append('-s')
  129. make_command.extend([
  130. '-j',
  131. str(parallel),
  132. '-r',
  133. '-R',
  134. '-f',
  135. 'build_keyboard.mk',
  136. ])
  137. if bootloader:
  138. make_command.append(bootloader)
  139. for key, value in env_vars.items():
  140. make_command.append(f'{key}={value}')
  141. make_command.extend([
  142. f'KEYBOARD={user_keymap["keyboard"]}',
  143. f'KEYMAP={user_keymap["keymap"]}',
  144. f'KEYBOARD_FILESAFE={keyboard_filesafe}',
  145. f'TARGET={target}',
  146. f'KEYBOARD_OUTPUT={keyboard_output}',
  147. f'KEYMAP_OUTPUT={keymap_output}',
  148. f'MAIN_KEYMAP_PATH_1={keymap_output}',
  149. f'MAIN_KEYMAP_PATH_2={keymap_output}',
  150. f'MAIN_KEYMAP_PATH_3={keymap_output}',
  151. f'MAIN_KEYMAP_PATH_4={keymap_output}',
  152. f'MAIN_KEYMAP_PATH_5={keymap_output}',
  153. f'KEYMAP_C={keymap_c}',
  154. f'KEYMAP_PATH={keymap_dir}',
  155. f'VERBOSE={verbose}',
  156. f'COLOR={color}',
  157. 'SILENT=false',
  158. f'QMK_BIN={"bin/qmk" if "DEPRECATED_BIN_QMK" in os.environ else "qmk"}',
  159. ])
  160. return make_command
  161. def parse_configurator_json(configurator_file):
  162. """Open and parse a configurator json export
  163. """
  164. # FIXME(skullydazed/anyone): Add validation here
  165. user_keymap = json.load(configurator_file)
  166. orig_keyboard = user_keymap['keyboard']
  167. aliases = json_load(Path('data/mappings/keyboard_aliases.json'))
  168. if orig_keyboard in aliases:
  169. if 'target' in aliases[orig_keyboard]:
  170. user_keymap['keyboard'] = aliases[orig_keyboard]['target']
  171. if 'layouts' in aliases[orig_keyboard] and user_keymap['layout'] in aliases[orig_keyboard]['layouts']:
  172. user_keymap['layout'] = aliases[orig_keyboard]['layouts'][user_keymap['layout']]
  173. return user_keymap