commands.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. """Helper functions for commands.
  2. """
  3. import os
  4. import sys
  5. import shutil
  6. from itertools import islice
  7. from pathlib import Path
  8. from milc import cli
  9. import jsonschema
  10. from qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE
  11. from qmk.json_schema import json_load, validate
  12. from qmk.keyboard import keyboard_alias_definitions
  13. from qmk.util import maybe_exit
  14. from qmk.path import unix_style_path
  15. def find_make():
  16. """Returns the correct make command for this environment.
  17. """
  18. make_cmd = os.environ.get('MAKE')
  19. if not make_cmd:
  20. make_cmd = 'gmake' if shutil.which('gmake') else 'make'
  21. return make_cmd
  22. def get_make_parallel_args(parallel=1):
  23. """Returns the arguments for running the specified number of parallel jobs.
  24. """
  25. parallel_args = []
  26. if int(parallel) <= 0:
  27. # 0 or -1 means -j without argument (unlimited jobs)
  28. parallel_args.append('--jobs')
  29. elif int(parallel) > 1:
  30. parallel_args.append('--jobs=' + str(parallel))
  31. if int(parallel) != 1:
  32. # If more than 1 job is used, synchronize parallel output by target
  33. parallel_args.append('--output-sync=target')
  34. return parallel_args
  35. def parse_configurator_json(configurator_file):
  36. """Open and parse a configurator json export
  37. """
  38. user_keymap = json_load(configurator_file)
  39. # Validate against the jsonschema
  40. try:
  41. validate(user_keymap, 'qmk.keymap.v1')
  42. except jsonschema.ValidationError as e:
  43. cli.log.error(f'Invalid JSON keymap: {configurator_file} : {e.message}')
  44. maybe_exit(1)
  45. keyboard = user_keymap.get('keyboard', None)
  46. aliases = keyboard_alias_definitions()
  47. while keyboard in aliases:
  48. last_keyboard = keyboard
  49. keyboard = aliases[keyboard].get('target', keyboard)
  50. if keyboard == last_keyboard:
  51. break
  52. user_keymap['keyboard'] = keyboard
  53. return user_keymap
  54. def parse_env_vars(args):
  55. """Common processing for cli.args.env
  56. """
  57. envs = {}
  58. for env in args:
  59. if '=' in env:
  60. key, value = env.split('=', 1)
  61. envs[key] = value
  62. else:
  63. cli.log.warning('Invalid environment variable: %s', env)
  64. return envs
  65. def build_environment(args):
  66. envs = parse_env_vars(args)
  67. if HAS_QMK_USERSPACE:
  68. envs['QMK_USERSPACE'] = unix_style_path(Path(QMK_USERSPACE).resolve())
  69. return envs
  70. def in_virtualenv():
  71. """Check if running inside a virtualenv.
  72. Based on https://stackoverflow.com/a/1883251
  73. """
  74. active_prefix = getattr(sys, "base_prefix", None) or getattr(sys, "real_prefix", None) or sys.prefix
  75. return active_prefix != sys.prefix
  76. def get_chunks(it, size):
  77. """Break down a collection into smaller parts
  78. """
  79. it = iter(it)
  80. return iter(lambda: tuple(islice(it, size)), ())
  81. def dump_lines(output_file, lines, quiet=True, remove_repeated_newlines=False):
  82. """Handle dumping to stdout or file
  83. Creates parent folders if required
  84. """
  85. generated = '\n'.join(lines) + '\n'
  86. if remove_repeated_newlines:
  87. while '\n\n\n' in generated:
  88. generated = generated.replace('\n\n\n', '\n\n')
  89. if output_file and output_file.name != '-':
  90. output_file.parent.mkdir(parents=True, exist_ok=True)
  91. if output_file.exists():
  92. with open(output_file, 'r', encoding='utf-8', newline='\n') as f:
  93. existing = f.read()
  94. if existing == generated:
  95. if not quiet:
  96. cli.log.info(f'No changes to {output_file.name}.')
  97. return
  98. output_file.replace(output_file.parent / (output_file.name + '.bak'))
  99. with open(output_file, 'w', encoding='utf-8', newline='\n') as f:
  100. f.write(generated)
  101. # output_file.write_text(generated, encoding='utf-8', newline='\n') # `newline` needs Python 3.10
  102. if not quiet:
  103. cli.log.info(f'Wrote {output_file.name} to {output_file}.')
  104. else:
  105. print(generated)