5

In a GNU Makefile, there are two types of variables:

# simple variable (immediate evaluation):
VAR := some_assignment

# recursively expanded variable (deferred evaluation):
VAR = some_assignment

One may append to a recursively expanded variable using:

  IMMEDIATE += DEFERRED or IMMEDIATE

For the append operator, '+=', the right-hand side is considered immediate if the variable was previously set as a simple variable (':=' or '::='), and deferred otherwise.

Is there any way to prepend to a recursively expanded variable?

My motivating example is to introduce a new library ahead of other ones in $(LDLIBS):

  # Unfortunately, newlib gets added to the end rather than the beginning.
  LDLIBS += $(if $(later_condition),newlib.a)

  # Unfortunately, the expression is evaluated now rather than being deferred.
  LDLIBS := $(if $(later_condition),newlib.a) $(LDLIBS)
Hugues
  • 2,865
  • 1
  • 27
  • 39
  • Could you keep `LDLIBS` in another variable, say `LDLIBS_DEFAULT` and then do `LDLIBS = $(if ..., newlib.a $(LDLIBS_DEFAULT), $(LDLIBS_DEFAULT))`? – Zorawar Feb 20 '16 at 02:15
  • Right, an even simpler version is `LDLIBS = $(if ..., newlib.a) $(LDLIBS_DEFAULT)`. However, I'm hoping to do this after some other included Makefile has already set the standard `LDLIBS` variable. – Hugues Feb 21 '16 at 04:26
  • 1
    How about saving the contents of `LDLIBS` after it may have been set in an included makefile in an immediate variable? So: `LDLIBS_SAVED := $(LDLIBS)` and then `LDLIBS = $(if ..., newlib.a) $(LDLIBS_SAVED)`? – Zorawar Feb 21 '16 at 18:56
  • Any reason why `$LDLIBS` can't be immediate? – bobbogo Feb 22 '16 at 13:53
  • @bobbogo I don't know if I follow you right but if you mean why can't the libs be added after there are numerous reasons this is possible including one controlling another (loosely and probably poorly worded here in my exhausted and sleepy head). This also applies to linker flags (I just had this problem and I discovered how to do so from the GNU Makefile documentation). – Pryftan Oct 03 '19 at 14:28
  • @Zorawar As a programmer that was my immediate thought when I noticed it was not allowed. But I also suspected that it would be possible to prepend to the variable and in the documentation for (ironically) appending to a variable they noted something that would rather prepend to it (though they worded it differently). I just posted an answer on this though maybe it's not what the OP is entirely after. – Pryftan Oct 03 '19 at 14:29

2 Answers2

5

I've came across the same problem. My solution is quite hacky, and makes use of $(value) and $(eval) functions.

What was important for me, it preserves flavor (recursive) of the variable, so that neither prepended variable, nor original variable is expanded during this action:

# Macro for prepending to a recursively expanded variable.
#
# Usage:
# * Prepending "text" to the VAR variable:
#       $(call prepend,VAR,text)
#
# * Prepending "a word list" to the VAR variable -- remember 
#   to add any trailing separator character (e.g. space):
#       $(call prepend,VAR,a word list )
#
# * Prepending OTHER_VAR variable to the VAR variable -- use $$ 
#   to defer any variable expansions:
#       $(call prepend,VAR,$$(OTHER_VAR))

define prepend
$(eval $(1) = $(2)$(value $(1)))
endef

# Macro for appending to a recursively expanded variable.
#
# Usage:
# * Appending "text" to the VAR variable:
#       $(call append,VAR,text)
#
# * Appending "a word list" to the VAR variable -- remember
#   to add any heading separator character (e.g. space):
#       $(call append,VAR, a word list)
# 
# * Appending OTHER_VAR variable to the VAR variable -- use $$ 
#   to defer any variable expansions:
#       $(call append,VAR,$$(OTHER_VAR))

define append
$(eval $(1) = $(value $(1))$(2))
endef

Quick testcase:

A = A
B = B
VAR = $(A)

$(info before: VAR=$(VAR) | value(VAR)=$(value VAR) | $(flavor VAR))
$(call prepend,VAR,$$(B))
$(info after : VAR=$(VAR) | value(VAR)=$(value VAR) | $(flavor VAR))

And its execution:

before: VAR=A | value(VAR)=$(A) | recursive
after : VAR=BA | value(VAR)=$(B)$(A) | recursive
make: *** No targets.  Stop.

Additional notes:

  • My problem was actually related to the fact that GNU make adds a whitespace separator when appending.
  • Hacky solution, but there is no native GNU make feature for such problem.
Kuchara
  • 622
  • 7
  • 16
  • 1
    This is wonderful; thanks. After forgetting the `$(eval ...)` a few times, I found it useful to include it directly within the `define` function. Also I prefer the usage docs: `# $(call prepend,VARIABLE,string) # Prepend a string to a recursively expanded $(VARIABLE). # $(call prepend,list,add some words ) # Remember to add any trailing separator character (e.g. space). # $(call prepend,PATH,$$(var1):$$(var2):) # Use $$ to defer any variable expansions.` – Hugues May 03 '16 at 00:00
  • Good point about `$(eval ...)` inside `define`. I've updated my answer. However I prefer 'usage' to be as generic as possible. We could create some 'example' section later with more sophisticated examples, but I think for smart people that will be enough. ;-) One more point: GNU make docs says _text_, so I'm using this instead of _string_. – Kuchara May 05 '16 at 16:57
  • **Hacky solution, but there is no native GNU make feature for such problem** If you mean prepend to a variable that's not true (at least it's not now in 2019). See my answer on this: https://stackoverflow.com/a/58221246/9205647 - far cleaner and nicer in my mind though it's always a wonderful feeling when you come up with a hack to solve a problem the more clever the better. – Pryftan Oct 03 '19 at 14:32
  • 1
    @Pryftan your link just leads back to this question where you don't seen to have an answer posted. – Sam Liddicott Jul 25 '23 at 16:15
3

Use this:

# Expand a variable, properly escaping '$' characters.
expand = $(if $(findstring simple,$(flavor $1)),$(subst $$,$$$$,$($1)),$(value $1))

# Prepend to a variable, preserving flavor.
prepend = \
    $(if $(findstring simple,$(flavor $1)), \
    $(eval $1 := $2 $(call expand,$1)), \
    $(eval $1 = $2 $(call expand,$1)))

x := 1 $$ 2
$(call prepend,x,X)
$(info $x)

x = 1 $$ 2
$(call prepend,x,X)
$(info $x)

$(value) is not good enough because, in an $(eval) context, $ characters will be parsed as variable references.

alecov
  • 4,882
  • 2
  • 29
  • 55
  • Thanks; that's a very nice refinement. Wouldn't be best to omit the space between the `$2` and `$(call expand,$1)`, e.g., to allow preprending to a PATH variable which expects a ':' separator and no spaces? – Hugues Nov 12 '18 at 16:08
  • @Hugues: `prepend` could surely be implemented as you said, but the idea is to be consistent with the append operator (`+=`), which also introduces a space. – alecov Nov 12 '18 at 20:52
  • @alecov, nice improvement! I've just found that when you `x := 1`, and then `x += 1`, `$(flavor x)` becomes "undefined", but variable behave like "simple". So I propose to invert conditions to search for "recursive". – Kuchara Oct 07 '19 at 16:12
  • @Kuchara How so? `$(flavor x)` is definitely alright after these assignments. Could you provide a complete example? – alecov Oct 10 '19 at 05:39