4

How can I force a target-specific variable to be set immediately (line 2)? I want the output of make to be release and make debug to be debug.

X = release
debug: X = debug
debug: all

# the rest is included from an external file; cannot be changed
Y := $(X)
BUILD := $(Y)

all:
    @echo $(BUILD)
0
1

Your issue is due to how GNUMake parses a make file.

GNU make does its work in two distinct phases. During the first phase it reads all the makefiles, included makefiles, etc. and internalizes all the variables and their values, implicit and explicit rules, and constructs a dependency graph of all the targets and their prerequisites. During the second phase, make uses these internal structures to determine what targets will need to be rebuilt and to invoke the rules necessary to do so.

Reading Makefiles

It looks as if when you run make debug the dependency is to run all: which prints out the value as if you ran make all. What you need to do is modify your makefile so that all and debug both trigger the same dependency. You'll usually see something like

all: $(executable)
debug: $(executable)
$(executable): $(objs)
    <compile objects to executable>

debug never triggers all but in either case the executable is compiled.

As for your code:

X = release

debug: X = debug

Y = $(X)
BUILD = $(Y)

.PHONY: print
print:
        @echo $(BUILD)

all: print
debug: print

I had to make print be a phony dependency since its not an actual object being created. Otherwise this would be your dependency that both debug and all both require but compile differently depending on the flags you've set.

1

Yand BUILD variables are simply expanded variables because they're assigned using := (and not =), see the manual for more information. Basically the variable is assigned when the variable is defined, which is preventing you to do what you want.

But the target-specific variables should have precedence over the global ones, so overriding the BUILD variable will work, since it will shadow the global BUILD variable in the scope of the debug: (and therefore the all:) target:

X = release
debug: BUILD = debug
debug: all

Y := $(X)
BUILD := $(Y)

all:
    @echo $(BUILD)

Note that using debug: Y = debug instead doesn't work since BUILD itself is a simply expanded variable.

I'm not able to understand exactly why something like debug: X := debug doesn't work. But I suppose that the target-specific variables are resolved after the global simply expanded variables. This also probably the reason why the target-specific BUILD variable can shadow the global one in my solution.


Note that @jecxjo answer is partially wrong. What prevents you to override the assignment of Y (or BUILD) is that they are assigned with := and not =. I.e. your initial code using = instead of := does exactly what you want:

X = release
debug: X = debug
debug: all

# Note the '=' assignment instead of the ':='
Y = $(X)
BUILD = $(Y)

all:
    @echo $(BUILD)

This is actually that change which solves your problem in his answer, not rewriting the targets. But since you mentioned that these assignments are included in another file that you cannot edit, this is not a solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.