aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZach White <skullydazed@gmail.com>2021-01-16 15:13:04 -0800
committerGitHub <noreply@github.com>2021-01-16 15:13:04 -0800
commitd9785ec31339d7f80279fd3d1005f76689ed2f6a (patch)
tree01f9e771367cfcd18d347eef7f85ce78a3b5ba50
parentc628408688306ed3e970505268cc4a235af8a5ff (diff)
downloadqmk_firmware-d9785ec31339d7f80279fd3d1005f76689ed2f6a.tar.gz
qmk_firmware-d9785ec31339d7f80279fd3d1005f76689ed2f6a.zip
Improve the compile and flash subcommands (#11334)
* add support for --clean to compile and flash * compile standalone JSON keymaps without polluting the tree * Add support for passing environment vars to make * make flake8 happy * document changes to qmk compile and flash * add -e support to json export compiling * Fix python 3.6 * honor $MAKE * add support for parallel builds
-rw-r--r--docs/cli_commands.md8
-rw-r--r--lib/python/qmk/cli/chibios/confmigrate.py8
-rwxr-xr-xlib/python/qmk/cli/compile.py27
-rw-r--r--lib/python/qmk/cli/flash.py26
-rw-r--r--lib/python/qmk/commands.py93
-rw-r--r--lib/python/qmk/constants.py5
6 files changed, 137 insertions, 30 deletions
diff --git a/docs/cli_commands.md b/docs/cli_commands.md
index 61ce1aa2a..5ab49abd2 100644
--- a/docs/cli_commands.md
+++ b/docs/cli_commands.md
@@ -11,13 +11,13 @@ This command is directory aware. It will automatically fill in KEYBOARD and/or K
11**Usage for Configurator Exports**: 11**Usage for Configurator Exports**:
12 12
13``` 13```
14qmk compile <configuratorExport.json> 14qmk compile [-c] <configuratorExport.json>
15``` 15```
16 16
17**Usage for Keymaps**: 17**Usage for Keymaps**:
18 18
19``` 19```
20qmk compile -kb <keyboard_name> -km <keymap_name> 20qmk compile [-c] [-e <var>=<value>] -kb <keyboard_name> -km <keymap_name>
21``` 21```
22 22
23**Usage in Keyboard Directory**: 23**Usage in Keyboard Directory**:
@@ -82,13 +82,13 @@ This command is directory aware. It will automatically fill in KEYBOARD and/or K
82**Usage for Configurator Exports**: 82**Usage for Configurator Exports**:
83 83
84``` 84```
85qmk flash <configuratorExport.json> -bl <bootloader> 85qmk flash [-bl <bootloader>] [-c] [-e <var>=<value>] <configuratorExport.json>
86``` 86```
87 87
88**Usage for Keymaps**: 88**Usage for Keymaps**:
89 89
90``` 90```
91qmk flash -kb <keyboard_name> -km <keymap_name> -bl <bootloader> 91qmk flash -kb <keyboard_name> -km <keymap_name> [-bl <bootloader>] [-c] [-e <var>=<value>]
92``` 92```
93 93
94**Listing the Bootloaders** 94**Listing the Bootloaders**
diff --git a/lib/python/qmk/cli/chibios/confmigrate.py b/lib/python/qmk/cli/chibios/confmigrate.py
index eae294a0c..b9cfda961 100644
--- a/lib/python/qmk/cli/chibios/confmigrate.py
+++ b/lib/python/qmk/cli/chibios/confmigrate.py
@@ -13,7 +13,7 @@ def eprint(*args, **kwargs):
13 print(*args, file=sys.stderr, **kwargs) 13 print(*args, file=sys.stderr, **kwargs)
14 14
15 15
16fileHeader = """\ 16file_header = """\
17/* Copyright 2020 QMK 17/* Copyright 2020 QMK
18 * 18 *
19 * This program is free software: you can redistribute it and/or modify 19 * This program is free software: you can redistribute it and/or modify
@@ -77,7 +77,7 @@ def check_diffs(input_defs, reference_defs):
77 77
78 78
79def migrate_chconf_h(to_override, outfile): 79def migrate_chconf_h(to_override, outfile):
80 print(fileHeader.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile) 80 print(file_header.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile)
81 81
82 for override in to_override: 82 for override in to_override:
83 print("#define %s %s" % (override[0], override[1]), file=outfile) 83 print("#define %s %s" % (override[0], override[1]), file=outfile)
@@ -87,7 +87,7 @@ def migrate_chconf_h(to_override, outfile):
87 87
88 88
89def migrate_halconf_h(to_override, outfile): 89def migrate_halconf_h(to_override, outfile):
90 print(fileHeader.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile) 90 print(file_header.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile)
91 91
92 for override in to_override: 92 for override in to_override:
93 print("#define %s %s" % (override[0], override[1]), file=outfile) 93 print("#define %s %s" % (override[0], override[1]), file=outfile)
@@ -97,7 +97,7 @@ def migrate_halconf_h(to_override, outfile):
97 97
98 98
99def migrate_mcuconf_h(to_override, outfile): 99def migrate_mcuconf_h(to_override, outfile):
100 print(fileHeader.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile) 100 print(file_header.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile)
101 101
102 print("#include_next <mcuconf.h>\n", file=outfile) 102 print("#include_next <mcuconf.h>\n", file=outfile)
103 103
diff --git a/lib/python/qmk/cli/compile.py b/lib/python/qmk/cli/compile.py
index daee597d8..322ce6a25 100755
--- a/lib/python/qmk/cli/compile.py
+++ b/lib/python/qmk/cli/compile.py
@@ -2,7 +2,6 @@
2 2
3You can compile a keymap already in the repo or using a QMK Configurator export. 3You can compile a keymap already in the repo or using a QMK Configurator export.
4""" 4"""
5import subprocess
6from argparse import FileType 5from argparse import FileType
7 6
8from milc import cli 7from milc import cli
@@ -15,6 +14,9 @@ from qmk.commands import compile_configurator_json, create_make_command, parse_c
15@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.') 14@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
16@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') 15@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
17@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.") 16@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
17@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.")
18@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")
19@cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.")
18@cli.subcommand('Compile a QMK Firmware.') 20@cli.subcommand('Compile a QMK Firmware.')
19@automagic_keyboard 21@automagic_keyboard
20@automagic_keymap 22@automagic_keymap
@@ -25,18 +27,32 @@ def compile(cli):
25 27
26 If a keyboard and keymap are provided this command will build a firmware based on that. 28 If a keyboard and keymap are provided this command will build a firmware based on that.
27 """ 29 """
30 if cli.args.clean and not cli.args.filename and not cli.args.dry_run:
31 command = create_make_command(cli.config.compile.keyboard, cli.config.compile.keymap, 'clean')
32 # FIXME(skullydazed/anyone): Remove text=False once milc 1.0.11 has had enough time to be installed everywhere.
33 cli.run(command, capture_output=False, text=False)
34
35 # Build the environment vars
36 envs = {}
37 for env in cli.args.env:
38 if '=' in env:
39 key, value = env.split('=', 1)
40 envs[key] = value
41 else:
42 cli.log.warning('Invalid environment variable: %s', env)
43
44 # Determine the compile command
28 command = None 45 command = None
29 46
30 if cli.args.filename: 47 if cli.args.filename:
31 # If a configurator JSON was provided generate a keymap and compile it 48 # If a configurator JSON was provided generate a keymap and compile it
32 # FIXME(skullydazed): add code to check and warn if the keymap already exists when compiling a json keymap.
33 user_keymap = parse_configurator_json(cli.args.filename) 49 user_keymap = parse_configurator_json(cli.args.filename)
34 command = compile_configurator_json(user_keymap) 50 command = compile_configurator_json(user_keymap, parallel=cli.config.compile.parallel, **envs)
35 51
36 else: 52 else:
37 if cli.config.compile.keyboard and cli.config.compile.keymap: 53 if cli.config.compile.keyboard and cli.config.compile.keymap:
38 # Generate the make command for a specific keyboard/keymap. 54 # Generate the make command for a specific keyboard/keymap.
39 command = create_make_command(cli.config.compile.keyboard, cli.config.compile.keymap) 55 command = create_make_command(cli.config.compile.keyboard, cli.config.compile.keymap, parallel=cli.config.compile.parallel, **envs)
40 56
41 elif not cli.config.compile.keyboard: 57 elif not cli.config.compile.keyboard:
42 cli.log.error('Could not determine keyboard!') 58 cli.log.error('Could not determine keyboard!')
@@ -48,7 +64,8 @@ def compile(cli):
48 cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command)) 64 cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command))
49 if not cli.args.dry_run: 65 if not cli.args.dry_run:
50 cli.echo('\n') 66 cli.echo('\n')
51 compile = subprocess.run(command) 67 # FIXME(skullydazed/anyone): Remove text=False once milc 1.0.11 has had enough time to be installed everywhere.
68 compile = cli.run(command, capture_output=False, text=False)
52 return compile.returncode 69 return compile.returncode
53 70
54 else: 71 else:
diff --git a/lib/python/qmk/cli/flash.py b/lib/python/qmk/cli/flash.py
index d720d42e7..b3827e800 100644
--- a/lib/python/qmk/cli/flash.py
+++ b/lib/python/qmk/cli/flash.py
@@ -3,7 +3,6 @@
3You can compile a keymap already in the repo or using a QMK Configurator export. 3You can compile a keymap already in the repo or using a QMK Configurator export.
4A bootloader must be specified. 4A bootloader must be specified.
5""" 5"""
6import subprocess
7from argparse import FileType 6from argparse import FileType
8 7
9from milc import cli 8from milc import cli
@@ -37,6 +36,9 @@ def print_bootloader_help():
37@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') 36@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
38@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') 37@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
39@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.") 38@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
39@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.")
40@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")
41@cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.")
40@cli.subcommand('QMK Flash.') 42@cli.subcommand('QMK Flash.')
41@automagic_keyboard 43@automagic_keyboard
42@automagic_keymap 44@automagic_keymap
@@ -50,6 +52,20 @@ def flash(cli):
50 52
51 If bootloader is omitted the make system will use the configured bootloader for that keyboard. 53 If bootloader is omitted the make system will use the configured bootloader for that keyboard.
52 """ 54 """
55 if cli.args.clean and not cli.args.filename and not cli.args.dry_run:
56 command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, 'clean')
57 cli.run(command, capture_output=False)
58
59 # Build the environment vars
60 envs = {}
61 for env in cli.args.env:
62 if '=' in env:
63 key, value = env.split('=', 1)
64 envs[key] = value
65 else:
66 cli.log.warning('Invalid environment variable: %s', env)
67
68 # Determine the compile command
53 command = '' 69 command = ''
54 70
55 if cli.args.bootloaders: 71 if cli.args.bootloaders:
@@ -60,16 +76,16 @@ def flash(cli):
60 76
61 if cli.args.filename: 77 if cli.args.filename:
62 # Handle compiling a configurator JSON 78 # Handle compiling a configurator JSON
63 user_keymap = parse_configurator_json(cli.args.filename) 79 user_keymap = parse_configurator_json(cli.args.filename, parallel=cli.config.flash.parallel)
64 keymap_path = qmk.path.keymap(user_keymap['keyboard']) 80 keymap_path = qmk.path.keymap(user_keymap['keyboard'])
65 command = compile_configurator_json(user_keymap, cli.args.bootloader) 81 command = compile_configurator_json(user_keymap, cli.args.bootloader, **envs)
66 82
67 cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap']) 83 cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
68 84
69 else: 85 else:
70 if cli.config.flash.keyboard and cli.config.flash.keymap: 86 if cli.config.flash.keyboard and cli.config.flash.keymap:
71 # Generate the make command for a specific keyboard/keymap. 87 # Generate the make command for a specific keyboard/keymap.
72 command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, cli.args.bootloader) 88 command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, cli.args.bootloader, parallel=cli.config.flash.parallel, **envs)
73 89
74 elif not cli.config.flash.keyboard: 90 elif not cli.config.flash.keyboard:
75 cli.log.error('Could not determine keyboard!') 91 cli.log.error('Could not determine keyboard!')
@@ -81,7 +97,7 @@ def flash(cli):
81 cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command)) 97 cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command))
82 if not cli.args.dry_run: 98 if not cli.args.dry_run:
83 cli.echo('\n') 99 cli.echo('\n')
84 compile = subprocess.run(command) 100 compile = cli.run(command, capture_output=False, text=True)
85 return compile.returncode 101 return compile.returncode
86 102
87 else: 103 else:
diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py
index 4ec7d2ea5..ba225bf55 100644
--- a/lib/python/qmk/commands.py
+++ b/lib/python/qmk/commands.py
@@ -6,11 +6,26 @@ import platform
6import subprocess 6import subprocess
7import shlex 7import shlex
8import shutil 8import shutil
9from pathlib import Path
10
11from milc import cli
9 12
10import qmk.keymap 13import qmk.keymap
14from qmk.constants import KEYBOARD_OUTPUT_PREFIX
15
16
17def _find_make():
18 """Returns the correct make command for this environment.
19 """
20 make_cmd = os.environ.get('MAKE')
21
22 if not make_cmd:
23 make_cmd = 'gmake' if shutil.which('gmake') else 'make'
11 24
25 return make_cmd
12 26
13def create_make_command(keyboard, keymap, target=None): 27
28def create_make_command(keyboard, keymap, target=None, parallel=1, **env_vars):
14 """Create a make compile command 29 """Create a make compile command
15 30
16 Args: 31 Args:
@@ -24,41 +39,95 @@ def create_make_command(keyboard, keymap, target=None):
24 target 39 target
25 Usually a bootloader. 40 Usually a bootloader.
26 41
42 parallel
43 The number of make jobs to run in parallel
44
45 **env_vars
46 Environment variables to be passed to make.
47
27 Returns: 48 Returns:
28 49
29 A command that can be run to make the specified keyboard and keymap 50 A command that can be run to make the specified keyboard and keymap
30 """ 51 """
52 env = []
31 make_args = [keyboard, keymap] 53 make_args = [keyboard, keymap]
32 make_cmd = 'gmake' if shutil.which('gmake') else 'make' 54 make_cmd = _find_make()
33 55
34 if target: 56 if target:
35 make_args.append(target) 57 make_args.append(target)
36 58
37 return [make_cmd, ':'.join(make_args)] 59 for key, value in env_vars.items():
60 env.append(f'{key}={value}')
38 61
62 return [make_cmd, '-j', str(parallel), *env, ':'.join(make_args)]
39 63
40def compile_configurator_json(user_keymap, bootloader=None): 64
41 """Convert a configurator export JSON file into a C file 65def compile_configurator_json(user_keymap, parallel=1, **env_vars):
66 """Convert a configurator export JSON file into a C file and then compile it.
42 67
43 Args: 68 Args:
44 69
45 configurator_filename 70 user_keymap
46 The configurator JSON export file 71 A deserialized keymap export
47 72
48 bootloader 73 bootloader
49 A bootloader to flash 74 A bootloader to flash
50 75
76 parallel
77 The number of make jobs to run in parallel
78
51 Returns: 79 Returns:
52 80
53 A command to run to compile and flash the C file. 81 A command to run to compile and flash the C file.
54 """ 82 """
55 # Write the keymap C file 83 # Write the keymap.c file
56 qmk.keymap.write(user_keymap['keyboard'], user_keymap['keymap'], user_keymap['layout'], user_keymap['layers']) 84 keyboard_filesafe = user_keymap['keyboard'].replace('/', '_')
85 target = f'{keyboard_filesafe}_{user_keymap["keymap"]}'
86 keyboard_output = Path(f'{KEYBOARD_OUTPUT_PREFIX}{keyboard_filesafe}')
87 keymap_output = Path(f'{keyboard_output}_{user_keymap["keymap"]}')
88 c_text = qmk.keymap.generate_c(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
89 keymap_dir = keymap_output / 'src'
90 keymap_c = keymap_dir / 'keymap.c'
91
92 keymap_dir.mkdir(exist_ok=True, parents=True)
93 keymap_c.write_text(c_text)
57 94
58 # Return a command that can be run to make the keymap and flash if given 95 # Return a command that can be run to make the keymap and flash if given
59 if bootloader is None: 96 verbose = 'true' if cli.config.general.verbose else 'false'
60 return create_make_command(user_keymap['keyboard'], user_keymap['keymap']) 97 color = 'true' if cli.config.general.color else 'false'
61 return create_make_command(user_keymap['keyboard'], user_keymap['keymap'], bootloader) 98 make_command = [
99 _find_make(),
100 '-j',
101 str(parallel),
102 '-r',
103 '-R',
104 '-f',
105 'build_keyboard.mk',
106 ]
107
108 for key, value in env_vars.items():
109 make_command.append(f'{key}={value}')
110
111 make_command.extend([
112 f'KEYBOARD={user_keymap["keyboard"]}',
113 f'KEYMAP={user_keymap["keymap"]}',
114 f'KEYBOARD_FILESAFE={keyboard_filesafe}',
115 f'TARGET={target}',
116 f'KEYBOARD_OUTPUT={keyboard_output}',
117 f'KEYMAP_OUTPUT={keymap_output}',
118 f'MAIN_KEYMAP_PATH_1={keymap_output}',
119 f'MAIN_KEYMAP_PATH_2={keymap_output}',
120 f'MAIN_KEYMAP_PATH_3={keymap_output}',
121 f'MAIN_KEYMAP_PATH_4={keymap_output}',
122 f'MAIN_KEYMAP_PATH_5={keymap_output}',
123 f'KEYMAP_C={keymap_c}',
124 f'KEYMAP_PATH={keymap_dir}',
125 f'VERBOSE={verbose}',
126 f'COLOR={color}',
127 'SILENT=false',
128 ])
129
130 return make_command
62 131
63 132
64def parse_configurator_json(configurator_file): 133def parse_configurator_json(configurator_file):
diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py
index 94ab68e5e..2ddaa568a 100644
--- a/lib/python/qmk/constants.py
+++ b/lib/python/qmk/constants.py
@@ -1,5 +1,6 @@
1"""Information that should be available to the python library. 1"""Information that should be available to the python library.
2""" 2"""
3from os import environ
3from pathlib import Path 4from pathlib import Path
4 5
5# The root of the qmk_firmware tree. 6# The root of the qmk_firmware tree.
@@ -17,3 +18,7 @@ VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85'
17DATE_FORMAT = '%Y-%m-%d' 18DATE_FORMAT = '%Y-%m-%d'
18DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S %Z' 19DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S %Z'
19TIME_FORMAT = '%H:%M:%S' 20TIME_FORMAT = '%H:%M:%S'
21
22# Constants that should match their counterparts in make
23BUILD_DIR = environ.get('BUILD_DIR', '.build')
24KEYBOARD_OUTPUT_PREFIX = f'{BUILD_DIR}/obj_'