aboutsummaryrefslogtreecommitdiffstats
path: root/Makefile
blob: 9ca73b4ad783c683de80eebc1ab3e35a2b52d6e5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
ifndef VERBOSE
.SILENT:
endif

ifdef silent
    SILENT = $(silent)
endif

ifdef SILENT
    SUB_IS_SILENT := $(silent)
endif

override SILENT = false

ON_ERROR := error_occured=1

STARTING_MAKEFILE := $(firstword $(MAKEFILE_LIST))
ROOT_MAKEFILE := $(lastword $(MAKEFILE_LIST))
ROOT_DIR := $(dir $(ROOT_MAKEFILE))
ifeq ($(ROOT_DIR),)
    ROOT_DIR := .
endif
ABS_STARTING_MAKEFILE := $(abspath $(STARTING_MAKEFILE))
ABS_ROOT_MAKEFILE := $(abspath $(ROOT_MAKEFILE))
ABS_STARTING_DIR := $(dir $(ABS_STARTING_MAKEFILE))
ABS_ROOT_DIR := $(dir $(ABS_ROOT_MAKEFILE))
STARTING_DIR := $(subst $(ABS_ROOT_DIR),,$(ABS_STARTING_DIR))

PATH_ELEMENTS := $(subst /, ,$(STARTING_DIR))

MAKEFILE_INCLUDED=yes

define NEXT_PATH_ELEMENT
    $$(eval CURRENT_PATH_ELEMENT := $$(firstword  $$(PATH_ELEMENTS)))
    $$(eval PATH_ELEMENTS := $$(wordlist  2,9999,$$(PATH_ELEMENTS)))
endef

$(eval $(call NEXT_PATH_ELEMENT))

ifeq ($(CURRENT_PATH_ELEMENT),keyboards)
    $(eval $(call NEXT_PATH_ELEMENT))
    KEYBOARD := $(CURRENT_PATH_ELEMENT)
    $(eval $(call NEXT_PATH_ELEMENT))
    ifeq ($(CURRENT_PATH_ELEMENT),keymaps)
        $(eval $(call NEXT_PATH_ELEMENT))
        KEYMAP := $(CURRENT_PATH_ELEMENT)
    else ifneq ($(CURRENT_PATH_ELEMENT),)
        SUBPROJECT := $(CURRENT_PATH_ELEMENT)
        $(eval $(call NEXT_PATH_ELEMENT))
        ifeq ($(CURRENT_PATH_ELEMENT),keymaps)
            $(eval $(call NEXT_PATH_ELEMENT))
            KEYMAP := $(CURRENT_PATH_ELEMENT)
        endif
    endif
endif

# Only consider folders with makefiles, to prevent errors in case there are extra folders
KEYBOARDS := $(notdir $(patsubst %/Makefile,%,$(wildcard $(ROOT_DIR)/keyboards/*/Makefile)))

#Compability with the old make variables
ifdef keyboard
    KEYBOARD := $(keyboard)
endif
ifdef sub
    SUBPROJECT := $(sub)
endif
ifdef subproject
    SUBPROJECT := $(subproject)
endif
ifdef keymap
    KEYMAP := $(keymap)
endif

#$(info Keyboard: $(KEYBOARD))
#$(info Keymap: $(KEYMAP))
#$(info Subproject: $(SUBPROJECT))
#$(info Keyboards: $(KEYBOARDS))

.DEFAULT_GOAL := all
ifneq ($(KEYMAP),)
    ifeq ($(SUBPROJECT),)
        .DEFAULT_GOAL := $(KEYBOARD)-$(KEYMAP)
    else
        .DEFAULT_GOAL := $(KEYBOARD)-$(SUBPROJECT)-$(KEYMAP)
    endif
else ifneq ($(SUBPROJECT),)
    .DEFAULT_GOAL := $(KEYBOARD)-$(SUBPROJECT)-allkm
else ifneq ($(KEYBOARD),)
    .DEFAULT_GOAL := $(KEYBOARD)-allsp-allkm
endif


# Compare the start of the RULE_VARIABLE with the first argument($1)
# If the rules equals $1 or starts with $1-, RULE_FOUND is set to true
#     and $1 is removed from the RULE variable
# Otherwise the RULE_FOUND variable is set to false
# The function is a bit tricky, since there's no built in $(startswith) function
define COMPARE_AND_REMOVE_FROM_RULE_HELPER
    ifeq ($1,$$(RULE))
        RULE:=
        RULE_FOUND := true
    else
        STARTDASH_REMOVED=$$(subst START$1-,,START$$(RULE))
        ifneq ($$(STARTDASH_REMOVED),START$$(RULE))
            RULE_FOUND := true
            RULE := $$(STARTDASH_REMOVED)
        else
            RULE_FOUND := false
        endif
    endif
endef

COMPARE_AND_REMOVE_FROM_RULE = $(eval $(call COMPARE_AND_REMOVE_FROM_RULE_HELPER,$1))$(RULE_FOUND)


# Recursively try to find a match
# $1 The list to be checked
# If a match is found, then RULE_FOUND is set to true
# and MATCHED_ITEM to the item that was matched
define TRY_TO_MATCH_RULE_FROM_LIST_HELPER
    ifneq ($1,)
        ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,$$(firstword $1)),true)
            MATCHED_ITEM := $$(firstword $1)
        else 
            $$(eval $$(call TRY_TO_MATCH_RULE_FROM_LIST_HELPER,$$(wordlist 2,9999,$1)))
        endif
    endif
endef

TRY_TO_MATCH_RULE_FROM_LIST = $(eval $(call TRY_TO_MATCH_RULE_FROM_LIST_HELPER,$1))$(RULE_FOUND)

define ALL_IN_LIST_LOOP
    OLD_RULE$1 := $$(RULE)
    $$(eval $$(call $1,$$(ITEM$1)))
    RULE := $$(OLD_RULE$1)
endef

define PARSE_ALL_IN_LIST
    $$(foreach ITEM$1,$2,$$(eval $$(call ALL_IN_LIST_LOOP,$1)))
endef

define PARSE_RULE
    RULE := $1
    COMMANDS :=
    ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,allkb),true)
        $$(eval $$(call PARSE_ALL_KEYBOARDS))
    else ifeq ($$(call TRY_TO_MATCH_RULE_FROM_LIST,$$(KEYBOARDS)),true)
        $$(eval $$(call PARSE_KEYBOARD,$$(MATCHED_ITEM)))
    else ifneq ($$(KEYBOARD),)
        $$(eval $$(call PARSE_KEYBOARD,$$(KEYBOARD)))
    else
        $$(info make: *** No rule to make target '$1'. Stop.)
		exit 1
    endif
endef

# $1 = Keyboard
define PARSE_KEYBOARD
    CURRENT_KB := $1
    # A subproject is any keyboard subfolder with a makefile
    SUBPROJECTS := $$(notdir $$(patsubst %/Makefile,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(CURRENT_KB)/*/Makefile)))
    ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,allsp),true)
        $$(eval $$(call PARSE_ALL_SUBPROJECTS))
    else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,defaultsp),true)
        $$(eval $$(call PARSE_SUBPROJECT,defaultsp))
    else ifeq ($$(call TRY_TO_MATCH_RULE_FROM_LIST,$$(SUBPROJECTS)),true)
        $$(eval $$(call PARSE_SUBPROJECT,$$(MATCHED_ITEM)))
    else ifneq ($$(SUBPROJECT),)
        $$(eval $$(call PARSE_SUBPROJECT,$$(SUBPROJECT)))
    else 
        # If there's no matching subproject, we assume it's the default
        # This will allow you to leave the subproject part of the target out
        $$(eval $$(call PARSE_SUBPROJECT,defaultsp))
    endif
endef

define PARSE_ALL_KEYBOARDS
    $$(eval $$(call PARSE_ALL_IN_LIST,PARSE_KEYBOARD,$(KEYBOARDS)))
endef

# $1 Subproject
define PARSE_SUBPROJECT
    ifeq ($1,defaultsp)
        SUBPROJECT_DEFAULT=
        $$(eval include $(ROOT_DIR)/keyboards/$$(CURRENT_KB)/Makefile)
        CURRENT_SP := $$(SUBPROJECT_DEFAULT)
    else
        CURRENT_SP := $1
    endif
    # If current subproject is empty (the default was not defined), and we have a list of subproject
    # then make all
    ifeq ($$(CURRENT_SP),)
        ifneq ($$(SUBPROJECTS),)
            CURRENT_SP := allsp
         endif
    endif
    ifneq ($$(CURRENT_SP),allsp) 
        KEYMAPS := $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(CURRENT_KB)/keymaps/*/.)))
        ifneq ($$(CURRENT_SP),)
            SP_KEYMAPS := $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(CURRENT_KB)/$$(CURRENT_SP)/keymaps/*/.)))
            KEYMAPS := $$(sort $$(KEYMAPS) $$(SP_KEYMAPS))
        endif
        ifeq ($$(RULE),)
            $$(eval $$(call PARSE_ALL_KEYMAPS))
        else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,allkm),true)
            $$(eval $$(call PARSE_ALL_KEYMAPS))
        else ifeq ($$(call TRY_TO_MATCH_RULE_FROM_LIST,$$(KEYMAPS)),true)
            $$(eval $$(call PARSE_KEYMAP,$$(MATCHED_ITEM)))
        else ifneq ($$(KEYMAP),)
            $$(eval $$(call PARSE_KEYMAP,$$(KEYMAP)))
        else
            ifeq ($$(CURRENT_SP),)
                $$(info make: *** No rule to make target '$$(CURRENT_KB)-$$(RULE)'. Stop.)
            else
                $$(info make: *** No rule to make target '$$(CURRENT_KB)-$$(CURRENT_SP)-$$(RULE)'. Stop.)
            endif
			exit 1
        endif
    else
        $$(eval $$(call PARSE_ALL_IN_LIST,PARSE_SUBPROJECT,$(SUBPROJECTS)))
    endif
endef

define PARSE_ALL_SUBPROJECTS
    ifeq ($$(SUBPROJECTS),)
        $$(eval $$(call PARSE_SUBPROJECT,defaultsp))
    else
        $$(eval $$(call PARSE_ALL_IN_LIST,PARSE_SUBPROJECT,$$(SUBPROJECTS)))
    endif
endef

# $1 Keymap
define PARSE_KEYMAP
    CURRENT_KM = $1
    # The rest of the rule is the target
    # Remove the leading "-" from the target, as it acts as a separator
    MAKE_TARGET := $$(patsubst -%,%,$$(RULE))
    COMMAND := COMMAND_KEYBOARD_$$(CURRENT_KB)_SUBPROJECT_$(CURRENT_SP)_KEYMAP_$$(CURRENT_KM)
    COMMANDS += $$(COMMAND)
    ifeq ($$(CURRENT_SP),)
        KB_SP := $(CURRENT_KB)
    else
        KB_SP := $(CURRENT_KB)/$$(CURRENT_SP)
    endif
    KB_SP := $(BOLD)$$(KB_SP)$(NO_COLOR)
    MAKE_VARS := KEYBOARD=$$(CURRENT_KB) SUBPROJECT=$$(CURRENT_SP) KEYMAP=$$(CURRENT_KM)
    MAKE_VARS += VERBOSE=$(VERBOSE) COLOR=$(COLOR)
    MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f build_keyboard.mk $$(MAKE_TARGET)
    MAKE_MSG := Making $$(KB_SP) with keymap $(BOLD)$$(CURRENT_KM)$(NO_COLOR)
    ifneq ($$(MAKE_TARGET),)
        MAKE_MSG += and target $(BOLD)$$(MAKE_TARGET)$(NO_COLOR)
    endif
    MAKE_MSG_FORMAT := $(AWK) '{ printf "%-118s", $$$$0;}'
    COMMAND_true_$$(COMMAND) := \
        printf "$$(MAKE_MSG)" | \
        $$(MAKE_MSG_FORMAT); \
        LOG=$$$$($$(MAKE_CMD) $$(MAKE_VARS) SILENT=true 2>&1) ; \
        if [ $$$$? -gt 0 ]; \
            then $$(PRINT_ERROR_PLAIN); \
        elif [ "$$$$LOG" != "" ] ; \
            then $$(PRINT_WARNING_PLAIN); \
        else \
            $$(PRINT_OK); \
        fi;
    COMMAND_false_$$(COMMAND) := \
        printf "$$(MAKE_MSG)\n\n"; \
        $$(MAKE_CMD) $$(MAKE_VARS) SILENT=false;
endef

define PARSE_ALL_KEYMAPS
    $$(eval $$(call PARSE_ALL_IN_LIST,PARSE_KEYMAP,$$(KEYMAPS)))
endef

define SET_SILENT_MODE
    ifdef SUB_IS_SILENT
        SILENT_MODE := $(SUB_IS_SILENT)
    else ifeq ($$(words $$(COMMANDS)),1)
        SILENT_MODE := false
    else
        SILENT_MODE := true
    endif
endef

include $(ROOT_DIR)/message.mk

RUN_COMMAND = \
$(COMMAND_$(SILENT_MODE)_$(COMMAND))

# Allow specifying just the subproject, in the keyboard directory, which will compile all keymaps
SUBPROJECTS := $(notdir $(patsubst %/Makefile,%,$(wildcard ./*/Makefile)))
.PHONY: $(SUBPROJECTS)
$(SUBPROJECTS): %: %-allkm 

.PHONY: %
%: 
	cmp --version >/dev/null 2>&1; if [ $$? -gt 0 ]; then printf "$(MSG_NO_CMP)"; exit 1; fi;
	git submodule status --recursive 2>/dev/null | \
	while IFS= read -r x; do \
		case "$$x" in \
			\ *) ;; \
			*) printf "$(MSG_SUBMODULE_DIRTY)";break;; \
		esac \
	done
	$(eval $(call PARSE_RULE,$@))
	$(eval $(call SET_SILENT_MODE))
	+error_occured=0; \
	$(foreach COMMAND,$(COMMANDS),$(RUN_COMMAND)) \
	if [ $$error_occured -gt 0 ]; then printf "$(MSG_ERRORS)" & exit $$error_occured; fi
	

.PHONY: all
all: all-keyboards 

.PHONY: all-keyboards
all-keyboards: allkb-allsp-allkm

.PHONY: all-keyboards-defaults
all-keyboards-defaults: allkb-allsp-default


GIT_VERSION := $(shell git describe --abbrev=6 --dirty --always --tags 2>/dev/null || date +"%Y-%m-%d-%H:%M:%S")
BUILD_DATE := $(shell date +"%Y-%m-%d-%H:%M:%S")
$(shell echo '#define QMK_VERSION "$(GIT_VERSION)"' > $(ROOT_DIR)/quantum/version.h)
$(shell echo '#define QMK_BUILDDATE "$(BUILD_DATE)"' >> $(ROOT_DIR)/quantum/version.h)