qmk_firmware/lib/python/qmk/commands.py

127 lines
3.6 KiB
Python

"""Helper functions for commands.
"""
import os
import sys
import shutil
from itertools import islice
from pathlib import Path
from milc import cli
import jsonschema
from qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE
from qmk.json_schema import json_load, validate
from qmk.keyboard import keyboard_alias_definitions
from qmk.util import maybe_exit
def find_make():
"""Returns the correct make command for this environment.
"""
make_cmd = os.environ.get('MAKE')
if not make_cmd:
make_cmd = 'gmake' if shutil.which('gmake') else 'make'
return make_cmd
def get_make_parallel_args(parallel=1):
"""Returns the arguments for running the specified number of parallel jobs.
"""
parallel_args = []
if int(parallel) <= 0:
# 0 or -1 means -j without argument (unlimited jobs)
parallel_args.append('--jobs')
elif int(parallel) > 1:
parallel_args.append('--jobs=' + str(parallel))
if int(parallel) != 1:
# If more than 1 job is used, synchronize parallel output by target
parallel_args.append('--output-sync=target')
return parallel_args
def parse_configurator_json(configurator_file):
"""Open and parse a configurator json export
"""
user_keymap = json_load(configurator_file)
# Validate against the jsonschema
try:
validate(user_keymap, 'qmk.keymap.v1')
except jsonschema.ValidationError as e:
cli.log.error(f'Invalid JSON keymap: {configurator_file} : {e.message}')
maybe_exit(1)
keyboard = user_keymap['keyboard']
aliases = keyboard_alias_definitions()
while keyboard in aliases:
last_keyboard = keyboard
keyboard = aliases[keyboard].get('target', keyboard)
if keyboard == last_keyboard:
break
user_keymap['keyboard'] = keyboard
return user_keymap
def build_environment(args):
"""Common processing for cli.args.env
"""
envs = {}
for env in args:
if '=' in env:
key, value = env.split('=', 1)
envs[key] = value
else:
cli.log.warning('Invalid environment variable: %s', env)
if HAS_QMK_USERSPACE:
envs['QMK_USERSPACE'] = Path(QMK_USERSPACE).resolve()
return envs
def in_virtualenv():
"""Check if running inside a virtualenv.
Based on https://stackoverflow.com/a/1883251
"""
active_prefix = getattr(sys, "base_prefix", None) or getattr(sys, "real_prefix", None) or sys.prefix
return active_prefix != sys.prefix
def get_chunks(it, size):
"""Break down a collection into smaller parts
"""
it = iter(it)
return iter(lambda: tuple(islice(it, size)), ())
def dump_lines(output_file, lines, quiet=True):
"""Handle dumping to stdout or file
Creates parent folders if required
"""
generated = '\n'.join(lines) + '\n'
if output_file and output_file.name != '-':
output_file.parent.mkdir(parents=True, exist_ok=True)
if output_file.exists():
with open(output_file, 'r', encoding='utf-8', newline='\n') as f:
existing = f.read()
if existing == generated:
if not quiet:
cli.log.info(f'No changes to {output_file.name}.')
return
output_file.replace(output_file.parent / (output_file.name + '.bak'))
with open(output_file, 'w', encoding='utf-8', newline='\n') as f:
f.write(generated)
# output_file.write_text(generated, encoding='utf-8', newline='\n') # `newline` needs Python 3.10
if not quiet:
cli.log.info(f'Wrote {output_file.name} to {output_file}.')
else:
print(generated)