diff options
Diffstat (limited to 'lib/python/qmk/commands.py')
| -rw-r--r-- | lib/python/qmk/commands.py | 165 |
1 files changed, 137 insertions, 28 deletions
diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py index 3a35c1103..421453d83 100644 --- a/lib/python/qmk/commands.py +++ b/lib/python/qmk/commands.py | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | """ | 2 | """ |
| 3 | import json | 3 | import json |
| 4 | import os | 4 | import os |
| 5 | import sys | ||
| 5 | import shutil | 6 | import shutil |
| 6 | from pathlib import Path | 7 | from pathlib import Path |
| 7 | from subprocess import DEVNULL | 8 | from subprocess import DEVNULL |
| @@ -10,7 +11,7 @@ from time import strftime | |||
| 10 | from milc import cli | 11 | from milc import cli |
| 11 | 12 | ||
| 12 | import qmk.keymap | 13 | import qmk.keymap |
| 13 | from qmk.constants import KEYBOARD_OUTPUT_PREFIX | 14 | from qmk.constants import QMK_FIRMWARE, KEYBOARD_OUTPUT_PREFIX |
| 14 | from qmk.json_schema import json_load | 15 | from qmk.json_schema import json_load |
| 15 | 16 | ||
| 16 | time_fmt = '%Y-%m-%d-%H:%M:%S' | 17 | time_fmt = '%Y-%m-%d-%H:%M:%S' |
| @@ -51,7 +52,7 @@ def create_make_target(target, parallel=1, **env_vars): | |||
| 51 | for key, value in env_vars.items(): | 52 | for key, value in env_vars.items(): |
| 52 | env.append(f'{key}={value}') | 53 | env.append(f'{key}={value}') |
| 53 | 54 | ||
| 54 | return [make_cmd, '-j', str(parallel), *env, target] | 55 | return [make_cmd, *get_make_parallel_args(parallel), *env, target] |
| 55 | 56 | ||
| 56 | 57 | ||
| 57 | def create_make_command(keyboard, keymap, target=None, parallel=1, **env_vars): | 58 | def create_make_command(keyboard, keymap, target=None, parallel=1, **env_vars): |
| @@ -86,11 +87,17 @@ def create_make_command(keyboard, keymap, target=None, parallel=1, **env_vars): | |||
| 86 | return create_make_target(':'.join(make_args), parallel, **env_vars) | 87 | return create_make_target(':'.join(make_args), parallel, **env_vars) |
| 87 | 88 | ||
| 88 | 89 | ||
| 89 | def get_git_version(repo_dir='.', check_dir='.'): | 90 | def get_git_version(current_time, repo_dir='.', check_dir='.'): |
| 90 | """Returns the current git version for a repo, or the current time. | 91 | """Returns the current git version for a repo, or the current time. |
| 91 | """ | 92 | """ |
| 92 | git_describe_cmd = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags'] | 93 | git_describe_cmd = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags'] |
| 93 | 94 | ||
| 95 | if repo_dir != '.': | ||
| 96 | repo_dir = Path('lib') / repo_dir | ||
| 97 | |||
| 98 | if check_dir != '.': | ||
| 99 | check_dir = repo_dir / check_dir | ||
| 100 | |||
| 94 | if Path(check_dir).exists(): | 101 | if Path(check_dir).exists(): |
| 95 | git_describe = cli.run(git_describe_cmd, stdin=DEVNULL, cwd=repo_dir) | 102 | git_describe = cli.run(git_describe_cmd, stdin=DEVNULL, cwd=repo_dir) |
| 96 | 103 | ||
| @@ -100,23 +107,58 @@ def get_git_version(repo_dir='.', check_dir='.'): | |||
| 100 | else: | 107 | else: |
| 101 | cli.log.warn(f'"{" ".join(git_describe_cmd)}" returned error code {git_describe.returncode}') | 108 | cli.log.warn(f'"{" ".join(git_describe_cmd)}" returned error code {git_describe.returncode}') |
| 102 | print(git_describe.stderr) | 109 | print(git_describe.stderr) |
| 103 | return strftime(time_fmt) | 110 | return current_time |
| 111 | |||
| 112 | return current_time | ||
| 113 | |||
| 114 | |||
| 115 | def get_make_parallel_args(parallel=1): | ||
| 116 | """Returns the arguments for running the specified number of parallel jobs. | ||
| 117 | """ | ||
| 118 | parallel_args = [] | ||
| 119 | |||
| 120 | if int(parallel) <= 0: | ||
| 121 | # 0 or -1 means -j without argument (unlimited jobs) | ||
| 122 | parallel_args.append('--jobs') | ||
| 123 | else: | ||
| 124 | parallel_args.append('--jobs=' + str(parallel)) | ||
| 125 | |||
| 126 | if int(parallel) != 1: | ||
| 127 | # If more than 1 job is used, synchronize parallel output by target | ||
| 128 | parallel_args.append('--output-sync=target') | ||
| 104 | 129 | ||
| 105 | return strftime(time_fmt) | 130 | return parallel_args |
| 106 | 131 | ||
| 107 | 132 | ||
| 108 | def write_version_h(git_version, build_date, chibios_version, chibios_contrib_version): | 133 | def create_version_h(skip_git=False, skip_all=False): |
| 109 | """Generate and write quantum/version.h | 134 | """Generate version.h contents |
| 110 | """ | 135 | """ |
| 111 | version_h = [ | 136 | if skip_all: |
| 112 | f'#define QMK_VERSION "{git_version}"', | 137 | current_time = "1970-01-01-00:00:00" |
| 113 | f'#define QMK_BUILDDATE "{build_date}"', | 138 | else: |
| 114 | f'#define CHIBIOS_VERSION "{chibios_version}"', | 139 | current_time = strftime(time_fmt) |
| 115 | f'#define CHIBIOS_CONTRIB_VERSION "{chibios_contrib_version}"', | 140 | |
| 116 | ] | 141 | if skip_git: |
| 142 | git_version = "NA" | ||
| 143 | chibios_version = "NA" | ||
| 144 | chibios_contrib_version = "NA" | ||
| 145 | else: | ||
| 146 | git_version = get_git_version(current_time) | ||
| 147 | chibios_version = get_git_version(current_time, "chibios", "os") | ||
| 148 | chibios_contrib_version = get_git_version(current_time, "chibios-contrib", "os") | ||
| 149 | |||
| 150 | version_h_lines = f"""/* This file was automatically generated. Do not edit or copy. | ||
| 151 | */ | ||
| 152 | |||
| 153 | #pragma once | ||
| 154 | |||
| 155 | #define QMK_VERSION "{git_version}" | ||
| 156 | #define QMK_BUILDDATE "{current_time}" | ||
| 157 | #define CHIBIOS_VERSION "{chibios_version}" | ||
| 158 | #define CHIBIOS_CONTRIB_VERSION "{chibios_contrib_version}" | ||
| 159 | """ | ||
| 117 | 160 | ||
| 118 | version_h_file = Path('quantum/version.h') | 161 | return version_h_lines |
| 119 | version_h_file.write_text('\n'.join(version_h)) | ||
| 120 | 162 | ||
| 121 | 163 | ||
| 122 | def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_vars): | 164 | def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_vars): |
| @@ -149,13 +191,8 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va | |||
| 149 | keymap_dir.mkdir(exist_ok=True, parents=True) | 191 | keymap_dir.mkdir(exist_ok=True, parents=True) |
| 150 | keymap_c.write_text(c_text) | 192 | keymap_c.write_text(c_text) |
| 151 | 193 | ||
| 152 | # Write the version.h file | 194 | version_h = Path('quantum/version.h') |
| 153 | git_version = get_git_version() | 195 | version_h.write_text(create_version_h()) |
| 154 | build_date = strftime('%Y-%m-%d-%H:%M:%S') | ||
| 155 | chibios_version = get_git_version("lib/chibios", "lib/chibios/os") | ||
| 156 | chibios_contrib_version = get_git_version("lib/chibios-contrib", "lib/chibios-contrib/os") | ||
| 157 | |||
| 158 | write_version_h(git_version, build_date, chibios_version, chibios_contrib_version) | ||
| 159 | 196 | ||
| 160 | # Return a command that can be run to make the keymap and flash if given | 197 | # Return a command that can be run to make the keymap and flash if given |
| 161 | verbose = 'true' if cli.config.general.verbose else 'false' | 198 | verbose = 'true' if cli.config.general.verbose else 'false' |
| @@ -166,8 +203,7 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va | |||
| 166 | make_command.append('-s') | 203 | make_command.append('-s') |
| 167 | 204 | ||
| 168 | make_command.extend([ | 205 | make_command.extend([ |
| 169 | '-j', | 206 | *get_make_parallel_args(parallel), |
| 170 | str(parallel), | ||
| 171 | '-r', | 207 | '-r', |
| 172 | '-R', | 208 | '-R', |
| 173 | '-f', | 209 | '-f', |
| @@ -181,10 +217,6 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va | |||
| 181 | make_command.append(f'{key}={value}') | 217 | make_command.append(f'{key}={value}') |
| 182 | 218 | ||
| 183 | make_command.extend([ | 219 | make_command.extend([ |
| 184 | f'GIT_VERSION={git_version}', | ||
| 185 | f'BUILD_DATE={build_date}', | ||
| 186 | f'CHIBIOS_VERSION={chibios_version}', | ||
| 187 | f'CHIBIOS_CONTRIB_VERSION={chibios_contrib_version}', | ||
| 188 | f'KEYBOARD={user_keymap["keyboard"]}', | 220 | f'KEYBOARD={user_keymap["keyboard"]}', |
| 189 | f'KEYMAP={user_keymap["keymap"]}', | 221 | f'KEYMAP={user_keymap["keymap"]}', |
| 190 | f'KEYBOARD_FILESAFE={keyboard_filesafe}', | 222 | f'KEYBOARD_FILESAFE={keyboard_filesafe}', |
| @@ -223,3 +255,80 @@ def parse_configurator_json(configurator_file): | |||
| 223 | user_keymap['layout'] = aliases[orig_keyboard]['layouts'][user_keymap['layout']] | 255 | user_keymap['layout'] = aliases[orig_keyboard]['layouts'][user_keymap['layout']] |
| 224 | 256 | ||
| 225 | return user_keymap | 257 | return user_keymap |
| 258 | |||
| 259 | |||
| 260 | def git_get_username(): | ||
| 261 | """Retrieves user's username from Git config, if set. | ||
| 262 | """ | ||
| 263 | git_username = cli.run(['git', 'config', '--get', 'user.name']) | ||
| 264 | |||
| 265 | if git_username.returncode == 0 and git_username.stdout: | ||
| 266 | return git_username.stdout.strip() | ||
| 267 | |||
| 268 | |||
| 269 | def git_check_repo(): | ||
| 270 | """Checks that the .git directory exists inside QMK_HOME. | ||
| 271 | |||
| 272 | This is a decent enough indicator that the qmk_firmware directory is a | ||
| 273 | proper Git repository, rather than a .zip download from GitHub. | ||
| 274 | """ | ||
| 275 | dot_git_dir = QMK_FIRMWARE / '.git' | ||
| 276 | |||
| 277 | return dot_git_dir.is_dir() | ||
| 278 | |||
| 279 | |||
| 280 | def git_get_branch(): | ||
| 281 | """Returns the current branch for a repo, or None. | ||
| 282 | """ | ||
| 283 | git_branch = cli.run(['git', 'branch', '--show-current']) | ||
| 284 | if not git_branch.returncode != 0 or not git_branch.stdout: | ||
| 285 | # Workaround for Git pre-2.22 | ||
| 286 | git_branch = cli.run(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) | ||
| 287 | |||
| 288 | if git_branch.returncode == 0: | ||
| 289 | return git_branch.stdout.strip() | ||
| 290 | |||
| 291 | |||
| 292 | def git_is_dirty(): | ||
| 293 | """Returns 1 if repo is dirty, or 0 if clean | ||
| 294 | """ | ||
| 295 | git_diff_staged_cmd = ['git', 'diff', '--quiet'] | ||
| 296 | git_diff_unstaged_cmd = [*git_diff_staged_cmd, '--cached'] | ||
| 297 | |||
| 298 | unstaged = cli.run(git_diff_staged_cmd) | ||
| 299 | staged = cli.run(git_diff_unstaged_cmd) | ||
| 300 | |||
| 301 | return unstaged.returncode != 0 or staged.returncode != 0 | ||
| 302 | |||
| 303 | |||
| 304 | def git_get_remotes(): | ||
| 305 | """Returns the current remotes for a repo. | ||
| 306 | """ | ||
| 307 | remotes = {} | ||
| 308 | |||
| 309 | git_remote_show_cmd = ['git', 'remote', 'show'] | ||
| 310 | git_remote_get_cmd = ['git', 'remote', 'get-url'] | ||
| 311 | |||
| 312 | git_remote_show = cli.run(git_remote_show_cmd) | ||
| 313 | if git_remote_show.returncode == 0: | ||
| 314 | for name in git_remote_show.stdout.splitlines(): | ||
| 315 | git_remote_name = cli.run([*git_remote_get_cmd, name]) | ||
| 316 | remotes[name.strip()] = {"url": git_remote_name.stdout.strip()} | ||
| 317 | |||
| 318 | return remotes | ||
| 319 | |||
| 320 | |||
| 321 | def git_check_deviation(active_branch): | ||
| 322 | """Return True if branch has custom commits | ||
| 323 | """ | ||
| 324 | cli.run(['git', 'fetch', 'upstream', active_branch]) | ||
| 325 | deviations = cli.run(['git', '--no-pager', 'log', f'upstream/{active_branch}...{active_branch}']) | ||
| 326 | return bool(deviations.returncode) | ||
| 327 | |||
| 328 | |||
| 329 | def in_virtualenv(): | ||
| 330 | """Check if running inside a virtualenv. | ||
| 331 | Based on https://stackoverflow.com/a/1883251 | ||
| 332 | """ | ||
| 333 | active_prefix = getattr(sys, "base_prefix", None) or getattr(sys, "real_prefix", None) or sys.prefix | ||
| 334 | return active_prefix != sys.prefix | ||
