How do I force a target to be rebuilt if a variable is set?

How do I force a target to be rebuilt if a variable is set?

Problem Description:

Assume I have a build-target foo:

foo:foo.c
    $(CC) $(CFLAGS) $(ARGS) -c foo.c -o foo

Now, ARGS is something that I pass on the command line:

$ make ARGS:=-DX=1 foo

So, I need to bypass make’s cleverness, because the foo target does not only depend on which files have changed, but also on the value of ARGS.

Is there something in make to do this? My hack (see answer) doesn’t seem to be the most elegant but it works. Anything better?

Solution – 1

My solution was to create a dummy phony target:

.PHONY:dummy
dummy:
    @:

and have foo depend on dummy if ARGS is nonempty:

foo:foo.c $(patsubst %,dummy,$(ARGS))

Solution – 2

Here is a general solution to your specific problem.

You want to be able to depend on a variable as a prerequisite. That is, you can make it a prerequisite to any target in your makefile, and when the value of the variable changes, you rebuild those targets.

Here is a function that does that, you use this function to declare a variable to be dependable, and then you can use it as a prerequisite.

Note that if the variable is not used on the command line, it will still mean that variable still has a value, namely, the empty string.

define DEPENDABLE_VAR

.PHONY: phony
$1: phony
    @if [[ `cat $1 2>&1` != '$($1)' ]]; then 
        echo -n $($1) > $1 ; 
    fi

endef

#declare ARGS to be dependable
$(eval $(call DEPENDABLE_VAR,ARGS))


foo:foo.c ARGS
    $(CC) $(CFLAGS) $(ARGS) -c foo.c -o foo

In fact, we could omit the need for “declaration”, and just write a similar function that will make all variables dependable by default. But I don’t like that. I prefer that the users that modify makefiles I write, declare their intentions explicitly. It is good for them 🙂

Solution – 3

Note on Mark’s excellent answer

The bare necessities of Mark’s answer are actually very simple. It really boils down to just:

.PHONY: phony
ARGS: phony
    @if [[ `cat ARGS 2>&1` != '$(ARGS)' ]]; then echo -n $(ARGS) >ARGS; fi

The rest of his code is just to let you reproduce the recipe for other variable names without repeating yourself. Useful in practice, but the above version will help you see what’s going on more easily.

In fact, my answer can even be made general (like Mark’s) for any variable name, but in a less complicated way as follows:

.PHONY: phony
.ARG~%: phony
    @if [[ `cat .ARG~$* 2>&1` != '$($*)' ]]; then echo -n $($*) >.ARG~$*; fi

Then you simply add .ARG~MYVARNAME as a dependency to any target to make that target depend on variable MYVARNAME.

Note that the dot in front of .ARG~% simply causes it to create a dependency-tracking file that is ‘hidden’ in linux.

So in your case, you would do:

foo: foo.c .ARG~ARGS
    $(CC) $(CFLAGS) $(ARGS) -c foo.c -o foo

Solution – 4

I don’t understand how the other solutions are supposed to work. If the ARGS target is .PHONY or depends on a .PHONY, then it will always be run, right?

Here is my solution using the $(file) function in newer versions of gmake:

.PHONY: FORCE

define DEPENDABLE_VAR
$(1):
        echo -n $($(1)) > $(1)
ifneq ("$(file <$(1))","$($(1))")
$(1): FORCE
endif

endef

#declare ARGS to be dependable
$(eval $(call DEPENDABLE_VAR,ARGS))

foo: foo.c ARGS
        touch foo

And the result:

~/stuff/makevars> make foo ARGS=1
echo -n 1 > ARGS
touch foo
~/stuff/makevars> make foo ARGS=1
make: 'foo' is up to date.
~/stuff/makevars> make foo ARGS=2
echo -n 2 > ARGS
touch foo
~/stuff/makevars> make foo ARGS=2
make: 'foo' is up to date.
Rate this post
We use cookies in order to give you the best possible experience on our website. By continuing to use this site, you agree to our use of cookies.
Accept
Reject