John Smith
John Smith

Reputation: 469

Is it possible to suppress variable inheritance for a specific prerequisite?

I have a C++ project that produces a shared library and I want to add tests to it, in a separate executable that dynamically links with the library. So the relevant part of the Makefile looks like this:

libstuff.so: stuff.o
    $(LINK_SHARED)

test: LDLFAGS += -L$(CURDIR) -Wl,-rpath=$(CURDIR) -lstuff
test: libstuff.so
test: test.o
    $(LINK)

check:: test
    ./test

LINK_SHARED and LINK are defined to invoke the compiler in an appropriate fashion. The rpath magic is to make sure the dynamic linker links with the exact version of the library I just compiled and want to test, not with the installed one.

The problem is that by default all target-specific variables are inherited by the prerequisites, so libstuff.so gets -lstuff in its LDFLAGS and the linker complains that it can't find it (duh!).

Is there a way to say that I don't want a specific prerequisite to inherit my variables and be used for dependency purposes only?

There are some hacky workarounds that I don't like. For example I can override LDFLAGS= in the libstuff.so specification, but then it wouldn't pick up the global LDFLAGS if I decide to define them.

There's a "private" modifier on variables, which would solve my particular problem with LDFLAGS but I'd still have other things like CFLAGS inherited (and I want it to be inherited by other prerequisites like the .o files). I want something like a private modifier on a prerequisite instead.

I can make the test executable statically link with the object files I'm testing, but I actually like the fact that I'm testing that I linked the shared object properly.

I can force make to start from a clean slate variable-wise by using recursive make invocation:

.PHONY: test_prerequisites
test_prerequisites:
    $(MAKE) testlib.so

Marking the target as phony makes it execute every time (as it should, since the original make can't know about its real dependencies or it would try to make them itself). An unfortunate side effect is that the test executable itself is rebuilt every time (though it's tolerable, and at least testlib.so is not rebuilt unless necessary).

Upvotes: 1

Views: 517

Answers (2)

John Smith
John Smith

Reputation: 469

Looks like it's impossible to do in a sane way, but I figured out a diabolical contraption that makes the recursive invocation solution work entirely correctly, thanks to @EtanReisner. Here's a complete runnable Makefile mock-up demonstrating the technique:

all:test

define LINK =
    @echo linking $@ with LDFLAGS=$(LDFLAGS)
    @touch $@
endef

stuff.o test.o:
    @echo compiling $@
    @touch $@

libstuff.so: stuff.o
    $(LINK)

.PHONY: FORCE

# A hack to avoid spilling LDFLAGS to prerequisites
.test_prerequisites.tmp: FORCE
    $(MAKE) .test_prerequisites2.tmp

.test_prerequisites2.tmp: libstuff.so
    touch .test_prerequisites.tmp .test_prerequisites2.tmp

test: LDFLAGS += -L$(CURDIR) -Wl,-rpath=$(CURDIR) -lstuff
test: .test_prerequisites.tmp
test: test.o
    $(LINK)

It uses two "witness" temporary files. When libstuff.so (or any other possible prerequisite) is modified, both witness files are updated and test is rebuilt.

However when libstuff.so is not updated, the first recipe is still always executed (thanks to having a phony prerequisite), then the recursive make invocation sees that the second witness doesn't need to be updated and doesn't execute its recipe, so the first witness is not updated either and test is not rebuilt.

Note: for this particular problem I decided to use the original, simpler recursive make solution because a) I happen to already have a phony target used to build all shared libraries in the project, and b) I pretty much always want the test binary to be rebuilt when running tests anyway, because I just changed something, the thing I want to test.

Upvotes: 1

arved
arved

Reputation: 4576

Add a Variable LDFLAGS_TEST and add it to the LINK command for test.

If you don't want to do this, see this similar question: Define make variable at rule execution time

Upvotes: 0

Related Questions