keyboard stuff
1ifndef VERBOSE
2.SILENT:
3endif
4
5# Never run this makefile in parallel, as it could screw things up
6# It won't affect the submakes, so you still get the speedup from specifying -jx
7.NOTPARALLEL:
8
9# Allow the silent with lower caps to work the same way as upper caps
10ifdef silent
11 SILENT = $(silent)
12endif
13
14ifdef SILENT
15 SUB_IS_SILENT := $(SILENT)
16endif
17
18# We need to make sure that silent is always turned off at the top level
19# Otherwise the [OK], [ERROR] and [WARN] messages won't be displayed correctly
20override SILENT := false
21
22ifeq ($(shell git rev-parse --is-inside-work-tree 2>/dev/null),)
23 export SKIP_GIT := yes
24 export NOT_REPO := yes
25endif
26
27ifdef SKIP_VERSION
28 export SKIP_GIT := yes
29endif
30
31ifndef SUB_IS_SILENT
32ifndef SKIP_GIT
33 QMK_VERSION := $(shell git describe --abbrev=0 --tags 2>/dev/null)
34endif
35
36ifneq ($(QMK_VERSION),)
37$(info QMK Firmware $(QMK_VERSION))
38endif
39endif
40
41# Try to determine userspace from qmk config, if set.
42ifeq ($(QMK_USERSPACE),)
43 QMK_USERSPACE = $(shell qmk config -ro user.overlay_dir | cut -d= -f2 | sed -e 's@^None$$@@g')
44endif
45
46# Determine which qmk cli to use
47QMK_BIN := qmk
48
49# avoid 'Entering|Leaving directory' messages
50MAKEFLAGS += --no-print-directory
51
52ON_ERROR := error_occurred=1
53
54BREAK_ON_ERRORS = no
55
56ROOT_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
57ifeq ($(ROOT_DIR),)
58 ROOT_DIR := .
59endif
60
61include paths.mk
62include $(BUILDDEFS_PATH)/support.mk
63
64TEST_OUTPUT_DIR := $(BUILD_DIR)/test
65ERROR_FILE := $(BUILD_DIR)/error_occurred
66
67.DEFAULT_GOAL := all:all
68
69
70# Compare the start of the RULE variable with the first argument($1)
71# If the rules equals $1 or starts with $1:, RULE_FOUND is set to true
72# and $1 is removed from the RULE variable
73# Otherwise the RULE_FOUND variable is set to false, and RULE left as it was
74# The function is a bit tricky, since there's no built in $(startswith) function
75define COMPARE_AND_REMOVE_FROM_RULE_HELPER
76 ifeq ($1,$$(RULE))
77 RULE:=
78 RULE_FOUND := true
79 else
80 STARTCOLON_REMOVED=$$(subst START$1:,,START$$(RULE))
81 ifneq ($$(STARTCOLON_REMOVED),START$$(RULE))
82 RULE_FOUND := true
83 RULE := $$(STARTCOLON_REMOVED)
84 else
85 RULE_FOUND := false
86 endif
87 endif
88endef
89
90# This makes it easier to call COMPARE_AND_REMOVE_FROM_RULE, since it makes it behave like
91# a function that returns the value
92COMPARE_AND_REMOVE_FROM_RULE = $(eval $(call COMPARE_AND_REMOVE_FROM_RULE_HELPER,$1))$(RULE_FOUND)
93
94# Try to find a match for the start of the rule to be checked
95# $1 The list to be checked
96# If a match is found, then RULE_FOUND is set to true
97# and MATCHED_ITEM to the item that was matched
98define TRY_TO_MATCH_RULE_FROM_LIST_HELPER
99 # Split on ":", padding with empty strings to avoid indexing issues
100 TOKEN1:=$$(shell python3 -c "import sys; print((sys.argv[1].split(':',1)+[''])[0])" $$(RULE))
101 TOKENr:=$$(shell python3 -c "import sys; print((sys.argv[1].split(':',1)+[''])[1])" $$(RULE))
102
103 FOUNDx:=$$(shell echo $1 | tr " " "\n" | grep -Fx $$(TOKEN1))
104 ifneq ($$(FOUNDx),)
105 RULE := $$(TOKENr)
106 RULE_FOUND := true
107 MATCHED_ITEM := $$(TOKEN1)
108 else
109 RULE_FOUND := false
110 MATCHED_ITEM :=
111 endif
112endef
113
114# Make it easier to call TRY_TO_MATCH_RULE_FROM_LIST
115TRY_TO_MATCH_RULE_FROM_LIST = $(eval $(call TRY_TO_MATCH_RULE_FROM_LIST_HELPER,$1))$(RULE_FOUND)
116
117# As TRY_TO_MATCH_RULE_FROM_LIST_HELPER, but with additional
118# resolution of keyboard_aliases.hjson for provided rule
119define TRY_TO_MATCH_RULE_FROM_LIST_HELPER_KB
120 # Split on ":", padding with empty strings to avoid indexing issues
121 TOKEN1:=$$(shell python3 -c "import sys; print((sys.argv[1].split(':',1)+[''])[0])" $$(RULE))
122 TOKENr:=$$(shell python3 -c "import sys; print((sys.argv[1].split(':',1)+[''])[1])" $$(RULE))
123
124 TOKEN1:=$$(shell $(QMK_BIN) resolve-alias --allow-unknown $$(TOKEN1))
125
126 FOUNDx:=$$(shell echo $1 | tr " " "\n" | grep -Fx $$(TOKEN1))
127 ifneq ($$(FOUNDx),)
128 RULE := $$(TOKENr)
129 RULE_FOUND := true
130 MATCHED_ITEM := $$(TOKEN1)
131 else
132 RULE_FOUND := false
133 MATCHED_ITEM :=
134 endif
135endef
136
137# Make it easier to call TRY_TO_MATCH_RULE_FROM_LIST_KB
138TRY_TO_MATCH_RULE_FROM_LIST_KB = $(eval $(call TRY_TO_MATCH_RULE_FROM_LIST_HELPER_KB,$1))$(RULE_FOUND)
139
140define ALL_IN_LIST_LOOP
141 OLD_RULE$1 := $$(RULE)
142 $$(eval $$(call $1,$$(ITEM$1)))
143 RULE := $$(OLD_RULE$1)
144endef
145
146define PARSE_ALL_IN_LIST
147 $$(foreach ITEM$1,$2,$$(eval $$(call ALL_IN_LIST_LOOP,$1)))
148endef
149
150# The entry point for rule parsing
151# parses a rule in the format <keyboard>:<keymap>:<target>
152# but this particular function only deals with the first <keyboard> part
153define PARSE_RULE
154 RULE := $1
155 COMMANDS :=
156 # If the rule starts with all, then continue the parsing from
157 # PARSE_ALL_KEYBOARDS
158 ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all),true)
159 KEYBOARD_RULE=all
160 $$(eval $$(call PARSE_ALL_KEYBOARDS))
161 else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,test),true)
162 $$(eval $$(call PARSE_TEST))
163 # If the rule starts with the name of a known keyboard, then continue
164 # the parsing from PARSE_KEYBOARD
165 else ifeq ($$(call TRY_TO_MATCH_RULE_FROM_LIST_KB,$$(shell $(QMK_BIN) list-keyboards)),true)
166 KEYBOARD_RULE=$$(MATCHED_ITEM)
167 $$(eval $$(call PARSE_KEYBOARD,$$(MATCHED_ITEM)))
168 else
169 $$(info make: *** No rule to make target '$1'. Stop.)
170 $$(info |)
171 $$(info | QMK's make format is:)
172 $$(info | make keyboard_folder:keymap_folder[:target])
173 $$(info |)
174 $$(info | Where `keyboard_folder` is the path to the keyboard relative to)
175 $$(info | `qmk_firmware/keyboards/`, and `keymap_folder` is the name of the)
176 $$(info | keymap folder under that board's `keymaps/` directory.)
177 $$(info |)
178 $$(info | Examples:)
179 $$(info | keyboards/dz60, keyboards/dz60/keymaps/default)
180 $$(info | -> make dz60:default)
181 $$(info | -> qmk compile -kb dz60 -km default)
182 $$(info | keyboards/planck/rev6, keyboards/planck/keymaps/default)
183 $$(info | -> make planck/rev6:default:flash)
184 $$(info | -> qmk flash -kb planck/rev6 -km default)
185 $$(info |)
186 endif
187endef
188
189# $1 = Keyboard
190# Parses a rule in the format <keymap>:<target>
191# the keyboard is already known when entering this function
192define PARSE_KEYBOARD
193 # If we want to compile the default subproject, then we need to
194 # include the correct makefile to determine the actual name of it
195 CURRENT_KB := $1
196
197 # 5/4/3/2/1
198 KEYBOARD_FOLDER_PATH_1 := $$(CURRENT_KB)
199 KEYBOARD_FOLDER_PATH_2 := $$(patsubst %/,%,$$(dir $$(KEYBOARD_FOLDER_PATH_1)))
200 KEYBOARD_FOLDER_PATH_3 := $$(patsubst %/,%,$$(dir $$(KEYBOARD_FOLDER_PATH_2)))
201 KEYBOARD_FOLDER_PATH_4 := $$(patsubst %/,%,$$(dir $$(KEYBOARD_FOLDER_PATH_3)))
202 KEYBOARD_FOLDER_PATH_5 := $$(patsubst %/,%,$$(dir $$(KEYBOARD_FOLDER_PATH_4)))
203
204 KEYMAPS :=
205 # get a list of all keymaps
206 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_1)/keymaps/*/.)))
207 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_2)/keymaps/*/.)))
208 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_3)/keymaps/*/.)))
209 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_4)/keymaps/*/.)))
210 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_5)/keymaps/*/.)))
211
212 ifneq ($(QMK_USERSPACE),)
213 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_1)/keymaps/*/.)))
214 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_2)/keymaps/*/.)))
215 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_3)/keymaps/*/.)))
216 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_4)/keymaps/*/.)))
217 KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_5)/keymaps/*/.)))
218 endif
219
220 KEYBOARD_LAYOUTS := $(shell $(QMK_BIN) list-layouts --keyboard $1)
221 LAYOUT_KEYMAPS :=
222 $$(foreach LAYOUT,$$(KEYBOARD_LAYOUTS),$$(eval LAYOUT_KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/layouts/*/$$(LAYOUT)/*/.)))))
223 ifneq ($(QMK_USERSPACE),)
224 $$(foreach LAYOUT,$$(KEYBOARD_LAYOUTS),$$(eval LAYOUT_KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/layouts/$$(LAYOUT)/*/.)))))
225 endif
226
227 KEYMAPS := $$(sort $$(KEYMAPS) $$(LAYOUT_KEYMAPS))
228
229 # if the rule after removing the start of it is empty (we haven't specified a kemap or target)
230 # compile all the keymaps
231 ifeq ($$(RULE),)
232 $$(eval $$(call PARSE_ALL_KEYMAPS))
233 # The same if all was specified
234 else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all),true)
235 $$(eval $$(call PARSE_ALL_KEYMAPS))
236 # List all keymaps for the given keyboard
237 else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,list-keymaps),true)
238 $$(eval $$(call LIST_ALL_KEYMAPS))
239 # Try to match the specified keyamp with the list of known keymaps
240 else ifeq ($$(call TRY_TO_MATCH_RULE_FROM_LIST,$$(KEYMAPS)),true)
241 $$(eval $$(call PARSE_KEYMAP,$$(MATCHED_ITEM)))
242 # Otherwise try to match the keymap from the current folder, or arguments to the make command
243 else ifneq ($$(KEYMAP),)
244 $$(eval $$(call PARSE_KEYMAP,$$(KEYMAP)))
245 # Otherwise if we are running make all:<user> just skip
246 else ifeq ($$(KEYBOARD_RULE),all)
247 # $$(info Skipping: No user keymap for $$(CURRENT_KB))
248 # Otherwise, make all keymaps, again this is consistent with how it works without
249 # any arguments
250 else
251 $$(eval $$(call PARSE_ALL_KEYMAPS))
252 endif
253endef
254
255# if we are going to compile all keyboards, match the rest of the rule
256# for each of them
257define PARSE_ALL_KEYBOARDS
258 $$(eval $$(call PARSE_ALL_IN_LIST,PARSE_KEYBOARD,$(shell $(QMK_BIN) list-keyboards)))
259endef
260
261# Prints a list of all known keymaps for the given keyboard
262define LIST_ALL_KEYMAPS
263 COMMAND_true_LIST_KEYMAPS := \
264 printf "$$(KEYMAPS)\n";
265 COMMAND_false_LIST_KEYMAPS := \
266 printf "$$(MSG_AVAILABLE_KEYMAPS)\n"; \
267 printf "$$(KEYMAPS)\n";
268 COMMANDS += LIST_KEYMAPS
269endef
270
271# $1 Keymap
272# This is the meat of compiling a keyboard, when entering this, everything is known
273# keyboard, subproject, and keymap
274# Note that we are not directly calling the command here, but instead building a list,
275# which will later be processed
276define PARSE_KEYMAP
277 CURRENT_KM = $1
278 # The rest of the rule is the target
279 # Remove the leading ":" from the target, as it acts as a separator
280 MAKE_TARGET := $$(patsubst :%,%,$$(RULE))
281 # We need to generate an unique identifier to append to the COMMANDS list
282 CURRENT_KB_UNDER := $$(subst /,_,$$(CURRENT_KB))
283 COMMAND := COMMAND_KEYBOARD_$$(CURRENT_KB_UNDER)_KEYMAP_$$(CURRENT_KM)
284 # If we are compiling a keyboard without a subproject, we want to display just the name
285 # of the keyboard, otherwise keyboard/subproject
286 KB_SP := $$(CURRENT_KB)
287 # Format it in bold
288 KB_SP := $(BOLD)$$(KB_SP)$(NO_COLOR)
289 # Specify the variables that we are passing forward to submake
290 MAKE_VARS := KEYBOARD=$$(CURRENT_KB) KEYMAP=$$(CURRENT_KM) QMK_BIN=$$(QMK_BIN)
291 # And the first part of the make command
292 MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f $(BUILDDEFS_PATH)/build_keyboard.mk $$(MAKE_TARGET)
293 # The message to display
294 MAKE_MSG := $$(MSG_MAKE_KB)
295 # We run the command differently, depending on if we want more output or not
296 # The true version for silent output and the false version otherwise
297 $$(eval $$(call BUILD))
298endef
299
300define BUILD
301 MAKE_VARS += VERBOSE=$(VERBOSE) COLOR=$(COLOR)
302 COMMANDS += $$(COMMAND)
303 COMMAND_true_$$(COMMAND) := \
304 printf "$$(MAKE_MSG)" | \
305 $$(MAKE_MSG_FORMAT); \
306 LOG=$$$$($$(MAKE_CMD) $$(MAKE_VARS) SILENT=true 2>&1) ; \
307 if [ $$$$? -gt 0 ]; \
308 then $$(PRINT_ERROR_PLAIN); \
309 elif [ "$$$$LOG" = "skipped" ] ; \
310 then $$(PRINT_SKIPPED_PLAIN); \
311 elif [ "$$$$LOG" != "" ] ; \
312 then $$(PRINT_WARNING_PLAIN); \
313 else \
314 $$(PRINT_OK); \
315 fi;
316 COMMAND_false_$$(COMMAND) := \
317 printf "$$(MAKE_MSG)\n\n"; \
318 $$(MAKE_CMD) $$(MAKE_VARS) SILENT=false; \
319 if [ $$$$? -gt 0 ]; \
320 then error_occurred=1; \
321 fi;
322endef
323
324# Just parse all the keymaps for a specific keyboard
325define PARSE_ALL_KEYMAPS
326 $$(eval $$(call PARSE_ALL_IN_LIST,PARSE_KEYMAP,$$(KEYMAPS)))
327endef
328
329define BUILD_TEST
330 TEST_PATH := $1
331 TEST_NAME := $$(notdir $$(TEST_PATH))
332 TEST_FULL_NAME := $$(subst /,_,$$(patsubst $$(ROOT_DIR)tests/%,%,$$(TEST_PATH)))
333 MAKE_TARGET := $2
334 COMMAND := $1
335 MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f $(BUILDDEFS_PATH)/build_test.mk $$(MAKE_TARGET)
336 MAKE_VARS := TEST=$$(TEST_NAME) TEST_OUTPUT=$$(TEST_FULL_NAME) TEST_PATH=$$(TEST_PATH) FULL_TESTS="$$(FULL_TESTS)"
337 MAKE_MSG := $$(MSG_MAKE_TEST)
338 $$(eval $$(call BUILD))
339 ifneq ($$(MAKE_TARGET),clean)
340 TEST_EXECUTABLE := $$(TEST_OUTPUT_DIR)/$$(TEST_FULL_NAME).elf
341 TESTS += $$(TEST_FULL_NAME)
342 TEST_MSG := $$(MSG_TEST)
343 $$(TEST_FULL_NAME)_COMMAND := \
344 printf "$$(TEST_MSG)\n"; \
345 $$(TEST_EXECUTABLE); \
346 if [ $$$$? -gt 0 ]; \
347 then error_occurred=1; \
348 fi; \
349 printf "\n";
350 endif
351endef
352
353define LIST_TEST
354 include $(BUILDDEFS_PATH)/testlist.mk
355 FOUND_TESTS := $$(patsubst ./tests/%,%,$$(TEST_LIST))
356 $$(info $$(FOUND_TESTS))
357endef
358
359define PARSE_TEST
360 TESTS :=
361 TEST_NAME := $$(firstword $$(subst :, ,$$(RULE)))
362 TEST_TARGET := $$(subst $$(TEST_NAME),,$$(subst $$(TEST_NAME):,,$$(RULE)))
363 include $(BUILDDEFS_PATH)/testlist.mk
364 ifeq ($$(TEST_NAME),all)
365 MATCHED_TESTS := $$(TEST_LIST)
366 else
367 MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring x$$(TEST_NAME)x, x$$(patsubst ./tests/%,%,$$(TEST)x)), $$(TEST),))
368 endif
369 $$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(TEST_TARGET))))
370endef
371
372
373# Set the silent mode depending on if we are trying to compile multiple keyboards or not
374# By default it's on in that case, but it can be overridden by specifying silent=false
375# from the command line
376define SET_SILENT_MODE
377 ifdef SUB_IS_SILENT
378 SILENT_MODE := $(SUB_IS_SILENT)
379 else ifeq ($$(words $$(COMMANDS)),1)
380 SILENT_MODE := false
381 else
382 SILENT_MODE := true
383 endif
384endef
385
386include $(BUILDDEFS_PATH)/message.mk
387
388ifeq ($(strip $(BREAK_ON_ERRORS)), yes)
389HANDLE_ERROR = exit 1
390else
391HANDLE_ERROR = echo $$error_occurred > $(ERROR_FILE)
392endif
393
394# The empty line is important here, as it will force a new shell to be created for each command
395# Otherwise the command line will become too long with a lot of keyboards and keymaps
396define RUN_COMMAND
397+error_occurred=0;\
398$(COMMAND_$(SILENT_MODE)_$(COMMAND))\
399if [ $$error_occurred -gt 0 ]; then $(HANDLE_ERROR); fi;
400
401
402endef
403define RUN_TEST
404+error_occurred=0;\
405$($(TEST)_COMMAND)\
406if [ $$error_occurred -gt 0 ]; then $(HANDLE_ERROR); fi;
407
408
409endef
410
411# Catch everything and parse the command line ourselves.
412.PHONY: %
413%:
414 # Ensure that $(QMK_BIN) works.
415 if ! $(QMK_BIN) hello 1> /dev/null 2>&1; then printf "$(MSG_PYTHON_MISSING)"; exit 1; fi
416ifdef NOT_REPO
417 printf "$(MSG_NOT_REPO)"
418endif
419ifndef SKIP_GIT
420 $(QMK_BIN) git-submodule --sync
421 # Check if the submodules are dirty, and display a warning if they are
422 if ! $(QMK_BIN) git-submodule --check 1> /dev/null 2>&1; then printf "$(MSG_SUBMODULE_DIRTY)"; fi
423endif
424 rm -f $(ERROR_FILE) > /dev/null 2>&1
425 $(eval $(call PARSE_RULE,$@))
426 $(eval $(call SET_SILENT_MODE))
427 # Run all the commands in the same shell, notice the + at the first line
428 # it has to be there to allow parallel execution of the submake
429 # This always tries to compile everything, even if error occurs in the middle
430 # But we return the error code at the end, to trigger travis failures
431 # The sort at this point is to remove duplicates
432 $(foreach COMMAND,$(sort $(COMMANDS)),$(RUN_COMMAND))
433 if [ -f $(ERROR_FILE) ]; then printf "$(MSG_ERRORS)" & exit 1; fi;
434 $(foreach TEST,$(sort $(TESTS)),$(RUN_TEST))
435 if [ -f $(ERROR_FILE) ]; then printf "$(MSG_ERRORS)" & exit 1; fi;
436
437lib/%:
438 git submodule sync $?
439 git submodule update --init $?
440
441.PHONY: git-submodule
442git-submodule:
443 $(QMK_BIN) git-submodule
444
445.PHONY: git-submodules
446git-submodules: git-submodule
447
448.PHONY: list-keyboards
449list-keyboards:
450 $(QMK_BIN) list-keyboards | tr '\n' ' '
451
452.PHONY: list-tests
453list-tests:
454 $(eval $(call LIST_TEST))
455
456.PHONY: generate-keyboards-file
457generate-keyboards-file:
458 $(QMK_BIN) list-keyboards
459
460.PHONY: clean
461clean:
462 echo -n 'Deleting .build/ ... '
463 rm -rf $(BUILD_DIR)
464 echo 'done.'
465
466.PHONY: distclean distclean_qmk
467distclean: distclean_qmk
468distclean_qmk: clean
469 echo -n 'Deleting *.bin, *.hex, and *.uf2 ... '
470 rm -f *.bin *.hex *.uf2
471 echo 'done.'
472
473ifneq ($(QMK_USERSPACE),)
474.PHONY: distclean_userspace
475distclean: distclean_userspace
476distclean_userspace: clean
477 echo -n 'Deleting userspace *.bin, *.hex, and *.uf2 ... '
478 rm -f $(QMK_USERSPACE)/*.bin $(QMK_USERSPACE)/*.hex $(QMK_USERSPACE)/*.uf2
479 echo 'done.'
480endif
481
482# Extra targets for formatting and/or pytest, running within the qmk/qmk_cli container to match GHA.
483CONTAINER_PREAMBLE := export HOME="/tmp"; export PATH="/tmp/.local/bin:\$$PATH"; python3 -m pip install --upgrade pip; python3 -m pip install -r requirements-dev.txt
484
485.PHONY: format-core
486format-core:
487 RUNTIME=docker ./util/docker_cmd.sh bash -lic "$(CONTAINER_PREAMBLE); qmk format-c --core-only -a && qmk format-python -a"
488
489.PHONY: pytest
490pytest:
491 RUNTIME=docker ./util/docker_cmd.sh bash -lic "$(CONTAINER_PREAMBLE); qmk pytest"
492
493.PHONY: format-and-pytest
494format-and-pytest:
495 RUNTIME=docker ./util/docker_cmd.sh bash -lic "$(CONTAINER_PREAMBLE); qmk format-c --core-only -a && qmk format-python -a && qmk pytest"