commands.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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_command(keyboard, keymap, target=None, parallel=1, silent=False, **env_vars):
  22. """Create a make compile command
  23. Args:
  24. keyboard
  25. The path of the keyboard, for example 'plank'
  26. keymap
  27. The name of the keymap, for example 'algernon'
  28. target
  29. Usually a bootloader.
  30. parallel
  31. The number of make jobs to run in parallel
  32. **env_vars
  33. Environment variables to be passed to make.
  34. Returns:
  35. A command that can be run to make the specified keyboard and keymap
  36. """
  37. make_cmd = [_find_make(), '--no-print-directory', '-r', '-R', '-C', './', '-f', 'build_keyboard.mk']
  38. env_vars['KEYBOARD'] = keyboard
  39. env_vars['KEYMAP'] = keymap
  40. env_vars['QMK_BIN'] = 'bin/qmk' if 'DEPRECATED_BIN_QMK' in os.environ else 'qmk'
  41. env_vars['VERBOSE'] = 'true' if cli.config.general.verbose else ''
  42. env_vars['SILENT'] = 'true' if silent else 'false'
  43. env_vars['COLOR'] = 'true' if cli.config.general.color else ''
  44. if parallel > 1:
  45. make_cmd.append('-j')
  46. make_cmd.append(parallel)
  47. if target:
  48. make_cmd.append(target)
  49. for key, value in env_vars.items():
  50. make_cmd.append(f'{key}={value}')
  51. return keyboard, keymap, make_cmd
  52. def get_git_version(repo_dir='.', check_dir='.'):
  53. """Returns the current git version for a repo, or the current time.
  54. """
  55. git_describe_cmd = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags']
  56. if Path(check_dir).exists():
  57. git_describe = cli.run(git_describe_cmd, stdin=DEVNULL, cwd=repo_dir)
  58. if git_describe.returncode == 0:
  59. return git_describe.stdout.strip()
  60. else:
  61. cli.log.warn(f'"{" ".join(git_describe_cmd)}" returned error code {git_describe.returncode}')
  62. print(git_describe.stderr)
  63. return strftime(time_fmt)
  64. return strftime(time_fmt)
  65. def write_version_h(git_version, build_date, chibios_version, chibios_contrib_version):
  66. """Generate and write quantum/version.h
  67. """
  68. version_h = [
  69. f'#define QMK_VERSION "{git_version}"',
  70. f'#define QMK_BUILDDATE "{build_date}"',
  71. f'#define CHIBIOS_VERSION "{chibios_version}"',
  72. f'#define CHIBIOS_CONTRIB_VERSION "{chibios_contrib_version}"',
  73. ]
  74. version_h_file = Path('quantum/version.h')
  75. version_h_file.write_text('\n'.join(version_h))
  76. def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_vars):
  77. """Convert a configurator export JSON file into a C file and then compile it.
  78. Args:
  79. user_keymap
  80. A deserialized keymap export
  81. bootloader
  82. A bootloader to flash
  83. parallel
  84. The number of make jobs to run in parallel
  85. Returns:
  86. A command to run to compile and flash the C file.
  87. """
  88. # Write the keymap.c file
  89. keyboard_filesafe = user_keymap['keyboard'].replace('/', '_')
  90. target = f'{keyboard_filesafe}_{user_keymap["keymap"]}'
  91. keyboard_output = Path(f'{KEYBOARD_OUTPUT_PREFIX}{keyboard_filesafe}')
  92. keymap_output = Path(f'{keyboard_output}_{user_keymap["keymap"]}')
  93. c_text = qmk.keymap.generate_c(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
  94. keymap_dir = keymap_output / 'src'
  95. keymap_c = keymap_dir / 'keymap.c'
  96. keymap_dir.mkdir(exist_ok=True, parents=True)
  97. keymap_c.write_text(c_text)
  98. # Write the version.h file
  99. git_version = get_git_version()
  100. build_date = strftime('%Y-%m-%d-%H:%M:%S')
  101. chibios_version = get_git_version("lib/chibios", "lib/chibios/os")
  102. chibios_contrib_version = get_git_version("lib/chibios-contrib", "lib/chibios-contrib/os")
  103. write_version_h(git_version, build_date, chibios_version, chibios_contrib_version)
  104. # Return a command that can be run to make the keymap and flash if given
  105. verbose = 'true' if cli.config.general.verbose else 'false'
  106. color = 'true' if cli.config.general.color else 'false'
  107. make_command = [_find_make()]
  108. if not cli.config.general.verbose:
  109. make_command.append('-s')
  110. make_command.extend([
  111. '-j',
  112. str(parallel),
  113. '-r',
  114. '-R',
  115. '-f',
  116. 'build_keyboard.mk',
  117. ])
  118. if bootloader:
  119. make_command.append(bootloader)
  120. for key, value in env_vars.items():
  121. make_command.append(f'{key}={value}')
  122. make_command.extend([
  123. f'GIT_VERSION={git_version}',
  124. f'BUILD_DATE={build_date}',
  125. f'CHIBIOS_VERSION={chibios_version}',
  126. f'CHIBIOS_CONTRIB_VERSION={chibios_contrib_version}',
  127. f'KEYBOARD={user_keymap["keyboard"]}',
  128. f'KEYMAP={user_keymap["keymap"]}',
  129. f'KEYBOARD_FILESAFE={keyboard_filesafe}',
  130. f'TARGET={target}',
  131. f'KEYBOARD_OUTPUT={keyboard_output}',
  132. f'KEYMAP_OUTPUT={keymap_output}',
  133. f'MAIN_KEYMAP_PATH_1={keymap_output}',
  134. f'MAIN_KEYMAP_PATH_2={keymap_output}',
  135. f'MAIN_KEYMAP_PATH_3={keymap_output}',
  136. f'MAIN_KEYMAP_PATH_4={keymap_output}',
  137. f'MAIN_KEYMAP_PATH_5={keymap_output}',
  138. f'KEYMAP_C={keymap_c}',
  139. f'KEYMAP_PATH={keymap_dir}',
  140. f'VERBOSE={verbose}',
  141. f'COLOR={color}',
  142. 'SILENT=false',
  143. f'QMK_BIN={"bin/qmk" if "DEPRECATED_BIN_QMK" in os.environ else "qmk"}',
  144. ])
  145. return user_keymap['keyboard'], user_keymap['keymap'], make_command
  146. def parse_configurator_json(configurator_file):
  147. """Open and parse a configurator json export
  148. """
  149. # FIXME(skullydazed/anyone): Add validation here
  150. user_keymap = json.load(configurator_file)
  151. orig_keyboard = user_keymap['keyboard']
  152. aliases = json_load(Path('data/mappings/keyboard_aliases.json'))
  153. if orig_keyboard in aliases:
  154. if 'target' in aliases[orig_keyboard]:
  155. user_keymap['keyboard'] = aliases[orig_keyboard]['target']
  156. if 'layouts' in aliases[orig_keyboard] and user_keymap['layout'] in aliases[orig_keyboard]['layouts']:
  157. user_keymap['layout'] = aliases[orig_keyboard]['layouts'][user_keymap['layout']]
  158. return user_keymap