This repository has been archived by the owner on Dec 6, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathprologue.mk
553 lines (491 loc) · 19.8 KB
/
prologue.mk
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
ifeq (, $(shell which md5sum))
# BSD derived systems (OS X, FreeBSD, etc.) use md5 instead of md5sum that
# is available on Linux
HASH := md5
else
HASH := md5sum
endif
# This is to precisely track what's going on.
# It's used by Make as command interpreter.
# By default, Make uses /bin/sh as interpreter for recipes.
# We overload that to pass everything through our script.
SHELL := $(QAKE_INCLUDE_DIR)/relay.sh
# This was to inhibit hash checking for phony targets.
# TODO: Remove this setting.
.SHELLFLAGS := --phony
# Check version of used Make.
# Most of the stuff mentioned here can actually be used in at least GNU Make 3.81.
#
# $(if) function checks first parameter:
# if it expands to non-empty string, $(if) expands to the second parameter.
# if it expands to empty string, $(if) expands to the third parameter
# (if present).
# More on conditional functions in GNU Make:
# http://www.gnu.org/software/make/manual/html_node/Conditional-Functions.html#Conditional-Functions
#
# $(shell) function executes shell command and expands
# to captured standard output.
# We effectively do make --version.
# We use $(MAKE) variable to account for different possible commands to launch
# Make. It will expand to absolute path to Make,
# so it works correctly with $PATH and everything.
# More about $(MAKE) variable:
# http://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html#MAKE-Variable
#
# So, to branch on result of shell command, we output something in one case
# (echo Fail) and nothing in another one.
# We also have to suppress all other output since otherwise the result of
# $(shell) won't be empty even when we didn't explicitly output anything.
#
# In case you wonder why I don't write comments in-line:
# GNU Make doesn't like that.
# It might want to cat the comment line to the previous one with backslash, etc.
$(if $(shell if ! $(MAKE) --version | grep 'GNU Make 4.' > /dev/null; \
then echo Fail; \
fi;),\
$(error Only GNU Make 4+ is supported))
# By default, when target is not specified, Make will build the one
# specified first in the Makefile.
# Confusing, let's disable and make it always build 'all' target.
# More about special variables:
# https://www.gnu.org/software/make/manual/html_node/Special-Variables.html
.DEFAULT_GOAL := all
# This is roughly equivalent to passing these flags on command line.
#
# -r removes built-in implicit rules (like %.o: %c ...).
# This is to ease debugging and improve performance.
# Make won't try every possible rule for every prerequisite anymore.
#
# -R removes built-in variables (like CC).
# Just to get rid of another source of confusion and make everything explicit.
#
# -j makes build parallel by default.
# Helps to discover underspecified dependencies and speeds up things.
#
# -O enables synchronization of output during parallel build.
# It is done on per-target basis: all commands of one target
# are grouped together.
#
# How to override these flags:
# make MAKEFLAGS=-j
# will specify only '-j'. You can also pass empty string.
# More about overriding any Make variable on the command line:
# https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding
MAKEFLAGS := -r -R -j -O -s
# Second expansion is used in this solution to seamlessly create
# directories for target files.
# It means prerequisite list will be re-expanded second time before
# executing the recipe. Usually prerequisites are evaluated just
# when reading the Makefile.
# Secondary expansion:
# https://www.gnu.org/software/make/manual/html_node/Secondary-Expansion.html
.SECONDEXPANSION:
# This is to use only one shell process for entire execution of recipe
# for target.
# We need it to store entire recipe in one .cmd file.
.ONESHELL:
# We'll introduce some convenience wrappers to ease debugging of
# our new features. They'll be enabled or disabled by this switch.
# Variables can be overridden by command-line arguments, like this:
# make BUILD_DEBUG=y
# More info:
# https://www.gnu.org/software/make/manual/html_node/Overriding.html
BUILD_DEBUG := n
# By default, GNU Make uses tab character to determine
# which line goes to recipe and which is Makefile syntax.
# Conventionally, recipe lines start with tab character.
# This is often source of confusion, as tab is not a printed character (obviously).
# This is even more so in the land of lousy editors which like
# to convert indentation from tabs to spaces and vice versa.
# With this, an actual printed character is used to introduce recipe line.
#
# GNU Make 4.0 required.
.RECIPEPREFIX := >
# Function: Print header of function call trace.
# Conditional parts of Makefiles:
# https://www.gnu.org/software/make/manual/html_node/Conditional-Syntax.html
# Undefined functions and variables are safe to call and reference -
# they just expand to empty string.
# $1 is first parameter of function as passed by Make.
# $0 is function name, and parameters are accessed as $2, $3 and so on.
# $(eval) evaluates what is being passed to it as syntax of Makefile.
# It allows one to implement "local variables".
# Such variables will-be re-evaluated on each expansion of function call.
# Usual variables (like BUILD_DEBUG above) are global and
# are visible from everywhere.
# Should we not use $(eval) here, FUNCTION_NAME would be defined by Make once
# when parsing the Makefile and not reset on each function call.
# More on $(eval) here:
# http://www.gnu.org/software/make/manual/html_node/Eval-Function.html#Eval-Function
#
# $(info) Outputs the given string to stdout.
# http://www.gnu.org/software/make/manual/html_node/Make-Control-Functions.html#Make-Control-Functions
ifeq ($(BUILD_DEBUG),y)
define FUNCTION_DEBUG_HEADER
$(eval FUNCTION_NAME := $1)
$(info Calling function $(FUNCTION_NAME):)
endef
endif
# Function: print variable assignment if build debugging is enabled.
# You might be wondering why I decided to start parameter trace line with '--'.
# It's because Make is very inconsistent about treatment of spaces in
# its' syntax. For example, $(info) will strip everything in front
# of first function parameters.
# To not go deeper inside Make's peculiar space and escaping rules,
# we'll just sidestep the problem by using dashes.
ifeq ($(BUILD_DEBUG),y)
define MAYBE_PRINT_VARIABLE_ASSIGNMENT
$(eval NAME := $1)
$(eval VALUE := $2)
$(info --$(NAME): $(VALUE))
endef
endif
# Function: Define a variable 'local' to the function.
#
# This is not a real local variable!
# It will have value it got during last function call later in Makefile.
# The only reason to do this is to perform name mangling to avoid
# variable name conflicts.
# And, well, more convenient function debugging.
#
# $(strip) function removed spaces on both ends of the variable value.
# This way we don't have to care about spaces added by make
# when continuing new lines.
# More here:
# https://www.gnu.org/software/make/manual/html_node/Splitting-Lines.html
#
# $(call) function performs expansion to the value of function definition,
# with $0, $1 and so on in the function definition being replaced
# with actual parameters.
#
# As you can see, variable names can be calculated via
# other variables and functions ($(FUNCTION)_$(NAME)).
define DEFINE_LOCAL_VARIABLE
$(eval FUNCTION := $(strip $1))
$(eval NAME := $(strip $2))
$(eval VALUE := $(strip $3))
$(call TRACE1,$(FUNCTION)_$(NAME) := $(VALUE))
endef
# Function: Alias to DEFINE_LOCAL_VARIABLE.
# We can use any character, except '#', ':', '=' and space
# in the name of variable.
# So, semantics: "move value on the right to the variable on the left"
# Okay, this is to illustrate our infinite possibilities,
# but in the end, let's stick with something more conventional.
# See below.
define <<
$(call DEFINE_LOCAL_VARIABLE,$1,$2,$3)
endef
# Old-school lisp-ish (or haskell-ish, or whatever) statement.
# Not really a statement: you still have to use $(call let,...).
define let
$(call DEFINE_LOCAL_VARIABLE,$1,$2,$3)
endef
# Function: Reference a local variable introduced by DEFINE_LOCAL_VARIABLE.
# We need $(strip) to remove extra spaces produced by newlines and $(eval)
# in the definition (result of the $(eval) here is empty).
define REFERENCE_LOCAL_VARIABLE
$(strip \
$(eval FUNCTION := $1)
$(eval NAME := $2)
$($(FUNCTION)_$(NAME))
)
endef
# Couldn't think of something more resembling of variable dereference
# and not breaking GNU Make at the same time ('*' does break it).
define &
$(call REFERENCE_LOCAL_VARIABLE,$1,$2)
endef
# Function: Print a single passed parameter to stdout if debugging is enabled.
ifeq ($(BUILD_DEBUG),y)
define PRINT1
$(info $1)
endef
endif
# Function: Print a value as it's being evaluated.
define TRACE1
$(call PRINT1,$1)
$(eval $1)
endef
# Directory creation:
# Use secondary expansion to get name of directory of target.
# We insert additional dollar sign to prevent expansion of $(@D) automatic variable.
# $(@D) contains directory part of the path to the target file.
# But it's so only when execution is inside of recipe part
# of target.
# That is, automatic variables like $@, $(@D) and so on, are target-local.
# More on this:
# https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
define DIRECTORY
$$(@D)/.directory.marker
endef
# Implicit rule for all marker files.
# % is called a stem of pattern rule.
# It matches the part of file path of target.
# I.e. if target is a/b/c/.directory.marker,
# % will match a/b/c/.
# We can't write a rule for directories themselves,
# since Make only matches targets by file paths,
# and path to directory looks just like path to any file.
# I.e., we would have to change first line to
# %:
# which obviously wouldn't work, since it would match any file.
# In case you think about rule matching on last slash and trying
# to create directories themselves: don't.
# Slashes processing rules are no less obscure than space processing rules,
# and there is a known bug during moving from Make 3.81 to Make 3.82
# due to changes in processing of paths with double slashes.
%/.directory.marker:
> $(call RUN,MKDIR $(@D),mkdir -p $(@D))
> touch $@
# Directory markers are precious:
# don't remove them if they were created by chains
# of implicit rules.
.PRECIOUS: %/.directory.marker
# This part sets target-specific shell flags.
# These are processed by relay.sh.
# TODO: Not all of them are actually required by now.
%/.directory.marker: .SHELLFLAGS = \
--target $@ --command-file [email protected] --prerequisites $? --
# We're going to put all the built stuff under 'build' directory.
# It allows to write easier .gitignore file and not accidentally commit
# built files to version control.
# It also makes implementation of 'clean' target trivial:
# just remove entire directory.
BUILD_DIR := build
AUX_DIR := $(BUILD_DIR)/aux
DU_DIR := $(AUX_DIR)/
RES_DIR := $(BUILD_DIR)/res
# This directory will hold sources.
# Good build system mirrors sources structure in build directory --
# it allows to manage pattern rules easier.
SRC_DIR := src
# Function: Wrap a command.
# Print short description, output command only if it failed.
# Last lines is the shell command that will be invoked when making some target
# that uses RUN in the recipe.
# If the command wasn't successful, we use tput to colorize part of the output.
# Double dollars are to prevent treatment as Make variable -
# one dollar will get eaten by expansion, second will be left and
# actually passed to the shell.
define RUN
$(strip \
$(call FUNCTION_DEBUG_HEADER,$0)
$(call let,$0,DESCRIPTION,$1)
$(call let,$0,COMMAND,$2)
)
echo $(call &,$0,DESCRIPTION); if ! $(call &,$0,COMMAND);\
then echo "$$(tput setaf 1)[ERROR]$$(tput sgr0) Failed command:\n$(call &,$0,COMMAND)"; fi
endef
define GET_TARGET_PATH
$(patsubst build/aux/%.cmd,%,$1)
endef
define GET_CMD_PATH
$(patsubst %,build/aux/%.cmd,$1)
endef
define GET_CMD_DID_UPDATE
$(patsubst %,build/aux/%.cmd.did_update,$1)
endef
# Function: Define build of a program.
#
# TODO: Update this documentation.
# It currently doesn't describe command tracking and dependencies hashing.
#
# Suppose we're calling this function as follows:
# $(call PROGRAM,a,b,c.c,d,e,f)
# (such flags make no sense, but we don't care, it's an example)
#
# We first define variable OBJ_b := build/c.c.o
#
# Then we define static pattern rule for objects:
# $(OBJ_b): build/%.o: \
# src/% \
# | $(DIRECTORY)
# > $(COMPILE_OBJECT)
#
# Details:
# https://www.gnu.org/software/make/manual/html_node/Static-Pattern.html
#
# Then we specify CFLAGS for these objects via target-specific variable assignment:
# https://www.gnu.org/software/make/manual/html_node/Target_002dspecific.html
#
# After that, we define DEP_b := $(OBJ_b:=.d).
# This is called a substitution reference.
# We substitute empty suffix of each list element with suffix '.d'.
# This way, we get 'build/c.c.o.d' in DEP_b.
# These are the dependency files as generated by GCC.
# They contain all headers on which given object file depends on.
#
# Next, we define $(PROGRAM_b): $(OBJ_b) to specify prerequisites of program
# and its' recipe on next line.
# LDFLAGS and LDLIBS are also specified in target-specific manner.
#
# Finally, we do ALL += $(PROGRAM_b) to make 'all' target build this program.
#
# You can see all the generated goodness by replacing
# $(eval $(call PROGRAM, ...)) with
# $(info $(call PROGRAM, ...))
define PROGRAM
$(call FUNCTION_DEBUG_HEADER,$0)
$(call let,$0,SOURCE_NAME,$1)
$(call let,$0,BUILT_NAME,$2)
$(call let,$0,SRC,$3)
$(call let,$0,CFLAGS,$4)
$(call let,$0,LDFLAGS,$5)
$(call let,$0,LDLIBS,$6)
.SHELLFLAGS = --target $$@
$(RES_DIR)/%: \
$(AUX_DIR)/%.cmd.did_update \
| $(AUX_DIR)/%.cmd \
$$(DIRECTORY)
> eval $$$$(cat $$(firstword $$|))
$(call TRACE1,OBJ_$(call &,$0,BUILT_NAME)_CMD := $(strip \
$(patsubst $(SRC_DIR)/$(call &,$0,SOURCE_NAME)%,\
$(AUX_DIR)/$(call &,$0,BUILT_NAME)/%.o.cmd,\
$(call &,$0,SRC))))
$(call TRACE1,OBJ_$(call &,$0,BUILT_NAME) := $(strip \
$(patsubst $(SRC_DIR)/$(call &,$0,SOURCE_NAME)%,\
$(RES_DIR)/$(call &,$0,BUILT_NAME)/%.o,\
$(call &,$0,SRC))))
$$(OBJ_$(call &,$0,BUILT_NAME)): \
$(RES_DIR)/$(call &,$0,BUILT_NAME)/%.o: \
$(call NORM_PATH,$(DU_DIR)/$(call &,$0,SOURCE_NAME))/%.did_update \
| $(call NORM_PATH,$(SRC_DIR)/$(call &,$0,SOURCE_NAME))/% \
$$(DIRECTORY)
$(OBJ_$(call &,$0,BUILT_NAME)_CMD): .SHELLFLAGS = \
--target $$@ --prerequisites $$? -- \
--build-dir $(BUILD_DIR)
$$(OBJ_$(call &,$0,BUILT_NAME)_CMD): \
$(AUX_DIR)/$(call &,$0,BUILT_NAME)/%.o.cmd: \
$(call NORM_PATH,$(DU_DIR)/$(call &,$0,SOURCE_NAME))/%.did_update \
$(THIS_MAKEFILE) \
| $(call NORM_PATH,$(SRC_DIR)/$(call &,$0,SOURCE_NAME))/% \
$$(DIRECTORY)
> echo '$$(COMPILE_OBJECT)' > $$@
.PRECIOUS: $$(OBJ_$(call &,$0,BUILT_NAME)_CMD)
$(OBJ_$(call &,$0,BUILT_NAME)_CMD): CFLAGS := $(call &,$0,CFLAGS)
$(OBJ_$(call &,$0,BUILT_NAME)_CMD): .SHELLFLAGS = \
--target $$@ --prerequisites $$? -- \
--build-dir $(BUILD_DIR)
$$(eval $$(call DEFINE_HASHED_CHAIN, \
OBJ_$$(call &,$0,BUILT_NAME), \
$(call NORM_PATH,./$(RES_DIR))/%, \
$(call NORM_PATH,./$(DU_DIR)/)/%, \
$$(OBJ_$(call &,$0,BUILT_NAME))))
$$(eval $$(call DEFINE_HASHED_CHAIN, \
SRC_$$(call &,$0,SOURCE_NAME), \
$(call NORM_PATH,./$(call &,$0,SOURCE_NAME))/%, \
$(call NORM_PATH,$(DU_DIR)/$(call &,$0,SOURCE_NAME))/%, \
$$(call &,$0,SRC)))
.PRECIOUS: build/aux/%.did_update
$(AUX_DIR)/%.did_update: \
$(AUX_DIR)/%.hash.new
> if [ -f $$(patsubst %.hash.new,\
%.hash.old,\
$$<) ];\
then \
if ! diff -q $$< $$(patsubst %.hash.new,\
%.hash.old,\
$$<) > /dev/null; \
then \
touch $$@; \
fi; \
else \
touch $$@; \
fi; \
cp $$< $$(patsubst %.hash.new,\
%.hash.old,\
$$<)
.PRECIOUS: build/aux/%.hash.new
$(AUX_DIR)/%.hash.new: \
$(RES_DIR)/% \
| $$(DIRECTORY)
> $(HASH) $$< > $$@
$(AUX_DIR)/%.hash.new: \
$(SRC_DIR)/% \
| $$(DIRECTORY)
> $(HASH) $$< > $$@
$(AUX_DIR)/%.hash.new: \
$(AUX_DIR)/% \
| $$(DIRECTORY)
> $(HASH) $$< > $$@
$(call TRACE1,DEP_$(call &,$0,BUILT_NAME) := $(strip \
$$(patsubst $(call NORM_PATH,$(RES_DIR)/$(BUILT_NAME))/%,$(call NORM_PATH,$(AUX_DIR)/$(BUILT_NAME))/%.d,$$(OBJ_$(call &,$0,BUILT_NAME)))))
-include $$(DEP_$(call &,$0,BUILT_NAME))
$(call TRACE1,PROGRAM_$(call &,$0,BUILT_NAME)_CMD := $(strip \
$(AUX_DIR)/$(call &,$0,BUILT_NAME)/$(call &,$0,BUILT_NAME)).cmd)
$(call TRACE1,PROGRAM_$(call &,$0,BUILT_NAME) := $(strip \
$(RES_DIR)/$(call &,$0,BUILT_NAME)/$(call &,$0,BUILT_NAME)))
$$(PROGRAM_$(call &,$0,BUILT_NAME)_CMD): \
$$(DID_UPDATE_OBJ_$(call &,$0,BUILT_NAME)) \
$(THIS_MAKEFILE) \
| $$(OBJ_$(call &,$0,BUILT_NAME))
> echo '$$(LINK_PROGRAM)' > $$@
$$(PROGRAM_$(call &,$0,BUILT_NAME)): \
$$(DID_UPDATE_OBJ_$(call &,$0,BUILT_NAME)) \
| $$(OBJ_$(call &,$0,BUILT_NAME))
.PRECIOUS: $$(PROGRAM_$(call &,$0,BUILT_NAME)_CMD)
$$(PROGRAM_$(call &,$0,BUILT_NAME)_CMD): LDFLAGS := $(call &,$0,LDFLAGS)
$$(PROGRAM_$(call &,$0,BUILT_NAME)_CMD): LDLIBS := $(call &,$0,LDLIBS)
$$(PROGRAM_$(call &,$0,BUILT_NAME)_CMD): .SHELLFLAGS = \
--target $$@ --prerequisites $$? -- \
--build-dir $(BUILD_DIR)
ALL += $$(PROGRAM_$(call &,$0,BUILT_NAME))
endef
# Funcion: define variables containing file paths
# of 'did update' markers and hashes.
define DEFINE_HASHED_CHAIN
$(strip \
$(call FUNCTION_DEBUG_HEADER,$0)
$(call let,$0,VAR_NAME_SUFFIX,$1)
$(call let,$0,SOURCE_PATTERN,$2)
$(call let,$0,TARGET_PATTERN,$3)
$(call let,$0,SOURCE_LIST,$4)
)
DID_UPDATE_$$(call &,$0,VAR_NAME_SUFFIX) = $$(strip \
$$(patsubst $(call &,$0,SOURCE_PATTERN),\
$(call &,$0,TARGET_PATTERN).did_update, \
$(call &,$0,SOURCE_LIST)))
HASH_NEW_$$(call &,$0,VAR_NAME_SUFFIX) = $$(strip \
$$(patsubst $(call &,$0,SOURCE_PATTERN),\
$(call &,$0,TARGET_PATTERN).hash.new, \
$(call &,$0,SOURCE_LIST)))
endef
# This is called 'canned recipe'.
# It's essentially a function, which will get its' automatic variables
# expanded in the context of target being built.
# Details:
# https://www.gnu.org/software/make/manual/html_node/Canned-Recipes.html
#
# $(@F) is file name part of the path of target.
# $< is first prerequisite of the target.
# $@ is the target.
# More on automatic variables:
# https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
#
define COMPILE_OBJECT
$(call RUN,GCC $$(notdir $$(call GET_TARGET_PATH,$$@)),gcc $$(CFLAGS) $$(patsubst $(AUX_DIR)/%.did_update,$(SRC_DIR)/%,$$<) -o $(RES_DIR)/$$(call GET_TARGET_PATH,$$@) -c -MD -MF $(AUX_DIR)/$$(call GET_TARGET_PATH,$$@).d -MP); \
sed -i -e "s|\\b$(patsubst $(AUX_DIR)/%.did_update,$(SRC_DIR)/%,$<)\\b||g" $(AUX_DIR)/$(call GET_TARGET_PATH,$@).d
endef
# Another canned recipe - for linking program out of objects.
# We still call 'gcc' instead of direct invocation of 'ld'
# since 'gcc' takes care about some additional parameters to 'ld' in many cases.
# Besides, there's 'collect2' in between of them.
# Just stick with compiler driver, it does the right thing most of the rime.
# $^ is all prerequisites of the target.
define LINK_PROGRAM
$(call RUN,GCC $$(notdir $$(call GET_TARGET_PATH,$$@)),gcc $$(LDFLAGS) $$(filter %.o,$$(patsubst $(AUX_DIR)/%.did_update,$(RES_DIR)/%,$$^)) -o $(RES_DIR)/$$(call GET_TARGET_PATH,$$@) $$(LDLIBS))
endef
# 'clean' just removes entire build directory.
.PHONY: clean
clean:
> $(call RUN,RM BUILD_DIR,rm -rf $(BUILD_DIR))
# This is a silly implementation of path normalization.
# We need this because there are paths with optional user-defined components.
# When these parts of paths are empty, we end up with double slashes and
# Make's path pattern matching stops working.
# FIXME: There should be better ways to do it, as currently it completely
# destroys performance (this is called several times per target).
define NORM_PATH
$(shell python -c 'import os, sys; print os.path.normpath(sys.argv[1])' $1)
endef