> > However, I'm not sure that this function is sufficiently generic to be > added as a built-in function. Can you provide use-cases for it? >
I suppose that depends on the definition of "sufficiently generic". The two definitions that come to mind: 1. Useful for many people who do build maintenance 2. Useful for people trying to improve performance on large builds or on makefile "libraries" (eg gmsl <https://gmsl.sourceforge.io/> or libmakefile <https://github.com/alperakcan/libmakefile>) If the chosen definition is (1) then you're right, but I think it's safe to say most people in (2) would find it useful (or would have). I'll try to give some good examples, though I came up with this years ago and I'm just getting around to contributing it. --- One of the more annoying problems I've encountered when writing canned expressions is whitespace creeping in. Here's a contrived example with a potential solution: $ cat makefile > F1 = ${${1}} > F2 = $(call ${0}_,$(strip ${1})) > F2_ = ${${1}} > TEXT = something > # note: the space before the word *TEXT* is helpful for readability but > causes a problem > $(info $(call F1, TEXT)) > $(warning $(call F2, TEXT)) > $ make --warn-undefined-variables > makefile:6: warning: undefined variable ' TEXT' > makefile:7: something Adding that extra level of indirection makes it easier to write the expression without worrying about unexpected whitespace but there's a cost that's easy to ignore if you don't have a straightforward way to measure it. --- One of the bottlenecks I uncovered in our build looked something like this: .RECIPEPREFIX := > some_target: > ${CC} *${SOMETHING_EXPENSIVE}* ${OTHER_FLAGS} ${^} -o ${@} The documentation says it but I didn't fully appreciate it until diving into this problem: the entire recipe undergoes expansion before a subshell gets created (either that or it occurs after vfork() and before execve(), but the effect is still the same). The non-obvious [to me] consequence: expensive operations in a recipe can artificially limit the number of jobs make can spawn/reap since time spent on recipe expansion is time not spent reaping/spawning jobs. The impact isn't as bad if the build is recursive, but that doesn't apply to us. --- After I created the *timeit* function I spent a few days coming up with / looking for awful contrivances to help me better understand what effect various expressions might have on time spent parsing them. Here's a few I remember, though I cannot guarantee these are functional as written -- I'm writing from memory without testing them: # Recursion: > F1 = $(strip $(if $(filter 16,${3}),${2},$(call ${0},${1},${${1}} > ${2},$(words ${2})))) > TEST = something > # prints the word "something" 16 times > $(info $(call F1,TEST,,)) # Automatically generate a target > define MAKE_TARGET > $(eval ${1}.o := $(addsuffix .o, $(basename $(wildcard ${2})))) > # evals needed because of *${${1}.o}* > $(eval ${1} : ${${1}.o}) > # ... whatever other strange contrivance comes to mind ... > endef # recursively expanded variable (B) with partial expansion. > A = a > B = ${A} > $(eval B=$(value B) ${B}) > # prints: ${A} a > $(info $(value B)) # variable reflection > THING = $(call ${0}_,$(filter $(firstword $(notdir $(basename > ${1}))).$(strip ${2}),${.VARIABLES}),$(strip ${2})) > THING_ = ${$(strip $(if ${1},${1},${2}))} > # Depending on its existence either *bar.CFLAGS* or *CFLAGS* gets > evaluated > bar : ; ${CC} $(call THING, ${@}, CFLAGS) ${^} -o ${@} # Conditional evaluation of macro arguments; *ifdef/ifndef* is from our > plugin F = $(ifndef 3,$(error ${0} requires 3 arguments))...stuff... Testing the effect they have on performance could be tricky. Without a way to evaluate timing of specific expressions you're limited to: 1. Build make / attach a profiler / figure out how to attribute performance changes to expressions used in the makefile 2. Use *time* (or something similar) / establish a baseline by removing the code in question 3. Cook up some makefile hackery that in some way measures time spent evaluating the expression (1) is probably not realistic. With (2): if expression A causes expression B to have performance problems, but B is in your baseline then the performance loss will be attributed to A whereas the problem may lie in B. Without a builtin or plugin function, (3) amounts to something like this: NOW = $(shell date +%s%N) > before := ${NOW} > ${EXPRESSION} > after := ${NOW} > $(info difference: $(shell expr ${after} - ${before})) ... which has limitations: - Relies on *$(shell)* which may skew/bias the results (though to be fair a builtin/plugin function could be added that returns a high resolution timestamp) - Would be more complicated to apply this to recipe parsing, *.SECONDEXPANSION* and other situations where temporary variables may be an issue, though you could get around it with something like: *$(info ${NOW})${EXPRESSION}$(info ${NOW})* and post-process the results Anyway, my diarrhea of the keyboard should probably end before I think of more to write. -brian
_______________________________________________ Bug-make mailing list Bug-make@gnu.org https://lists.gnu.org/mailman/listinfo/bug-make