StoneThrow
StoneThrow

Reputation: 6285

Is it possible to export a lazily-evaluated variable to a child Makefile?

This question is hopefully the last one I've been building up to from this and this.

I have the following directory structure:

.
├── Makefile
├── src
│   └── Makefile
└── test
    └── Makefile

My top-level Makefile just runs $(MAKE) on the src/ and test/ subdirs:

TOP_DIR := $(shell pwd)
export PRJ_LIBS = $(wildcard $(TOP_DIR)/build/*)

all: src test
        @echo $(PRJ_LIBS)

src:
        @$(MAKE) -C $@

test:
        @$(MAKE) -C $@

.PHONY: src test

My src/ Makefile creates a file in peer subdirectory build/:

all:
        @mkdir -p ../build
        @touch ../build/libfoo.so

I would like my test/ Makefile to be able to access the updated content of the build/ directory:

all:
        @echo $(PRJ_LIBS)

The above Makefiles don't achieve this:

$ rm -rf ./build/ && make
make[1]: Entering directory '/path/to/src'
make[1]: Leaving directory '/path/to/src'
make[1]: Entering directory '/path/to/test'

make[1]: Leaving directory '/path/to/test'

$ 

I figured out that one problem is that I was exporting $(PRJ_LIBS) in the top-level Makefile. Making this a non-exported variable improves things slightly: the updated content of the build/ subdir is visible in the top-level Makefile's all recipe, but still not in the test/ directory's Makefile:

...
#export PRJ_LIBS = $(wildcard $(TOP_DIR)/build/*)
PRJ_LIBS = $(wildcard $(TOP_DIR)/build/*)
...
$ rm -rf ./build/ && make
make[1]: Entering directory '/path/to/src'
make[1]: Leaving directory '/path/to/src'
make[1]: Entering directory '/path/to/test'

make[1]: Leaving directory '/path/to/test'
/path/to/build/libfoo.so

By my amateur estimation, it looks like I have competing goals here: I want to "export" my lazily-evaluated variable so that it's "known" by both the top-level Makefile and a child Makefile, but exporting the variable seems to have the effect of immediate/up-front evaluation.


An apparent workaround is to rely on an explicit call to a shell command:

#export PRJ_LIBS = $(wildcard $(TOP_DIR)/build/*)
#PRJ_LIBS = $(wildcard $(TOP_DIR)/build/*)
export PRJ_LIBS = $(shell find $(TOP_DIR)/build -type f)
$ rm -rf ./build/ && make
find: ‘/path/to/build’: No such file or directory
make[1]: Entering directory '/path/to/src'
make[1]: Leaving directory '/path/to/src'
make[1]: Entering directory '/path/to/test'
/path/to/build/libfoo.so
make[1]: Leaving directory '/path/to/test'
/path/to/build/libfoo.so
$ 

Though this achieves my goal, exporting the variable has that unpleasant side-effect of the up-front evaluation resulting in the eyesore find failure to stderr. Not exporting the variable has the same effect as the second trial, above: the variable being evaluated as desired in the top-level all recipe, but not in the test/ subdirectory's all recipe.


To someone more expert with Makefiles, what is the best or "well-known" solution to what I'm trying to achieve: define a variable in a top-level Makefile that can be lazily-evaluated in a child-level Makefile? Preferably with no side-effects like up-front shell command failures. (It's actually not important that the variable be evaluated in the top-level Makefile -- that was just more a debugging aid for me to understand things)

Upvotes: 1

Views: 248

Answers (1)

StoneThrow
StoneThrow

Reputation: 6285

I'd like to offer one solution from the results of unguided experimentation:

I can not export my lazy variable in the top-level Makefile, and then pass it as an argument to the child make, like so:

TOP_DIR := $(shell pwd)
PRJ_LIBS = $(wildcard $(TOP_DIR)/build/*)

all: src test
        @echo $(PRJ_LIBS)

src:
        @$(MAKE) -C $@

test:
        @$(MAKE) PRJ_LIBS=$(PRJ_LIBS) -C $@

.PHONY: src test
$ rm -rf ./build/ && make
make[1]: Entering directory '/home/amenon/code/fubar/src'
make[1]: Leaving directory '/home/amenon/code/fubar/src'
make[1]: Entering directory '/home/amenon/code/fubar/test'
/home/amenon/code/fubar/build/libfoo.so
make[1]: Leaving directory '/home/amenon/code/fubar/test'
/home/amenon/code/fubar/build/libfoo.so
$ 

I'm curious what Makefile experts make of this solution -- is this considered a good way to solve this?

Upvotes: 1

Related Questions