Nathaniel Waisbrot
Nathaniel Waisbrot

Reputation: 24523

Set a variable from within a rule

I have an external tool that fetches some sources (Rebar). I want to fill in a variable according to the contents of a directory after Rebar runs.

EFLAGS += -I$(PWD)/include 
EFLAGS += -pa $(PWD)/ebin
## $(PWD)/deps/* will only have contents after Rebar runs
EFLAGS += $(patsubst %,-pa %,$(wildcard $(PWD)/deps/*/ebin))

build-deps:
    ./rebar get-deps
    ./rebar compile

build-main: build-deps
    erlc $(EFLAGS) $(INFILE)

The above will work as intended if I run it as two separate invocations:

make build-deps
make build-main

However, if I just make build-main, then EFLAGS gets set while the deps/ directory is empty, then the directory is populated, and then I use EFLAGS.

Is there a good way for me to only set EFLAGS after I've run some rules?

EDIT: Here's a Makefile that may demonstrate the problem more easily:

A=$(wildcard test*)

foo:
    touch test1

bar: foo
    @echo $A

clean:
    -rm test*

Here, the "foo" target is standing in for my call to rebar, so just imagine that you don't know which files I'm going to pass to touch. If you try

make clean
make bar
make bar

you will find that the two invocations of make bar produce different results, because in the second one test1 exists before make begins. I'm looking for a way to get the output of the second make bar invocation immediately after running make clean.

Upvotes: 2

Views: 1545

Answers (2)

Etan Reisner
Etan Reisner

Reputation: 81052

I believe something like the following, using an included makefile, will work. Untested but I believe something along these lines will do what you want. Assuming you want to run rebar every time you try to build.

EFLAGS += -I$(PWD)/include 
EFLAGS += -pa $(PWD)/ebin
EFLAGS += $(PADIRS)

-include paflags.mk

build-main:
    erlc $(EFLAGS) $(INFILE)

paflags.mk: force
        ./rebar get-deps
        ./rebar compile
        echo 'PADIRS := $$(patsubst %,-pa %,$$(wildcard $$(PWD)/deps/*/ebin))' > '$@'

force: ;

Above edited to remove the .PHONY declaration on paflags.mk as that seemed to cause make to not perform the restart necessary for this trick to work.

Alternatively, since you aren't using any of make's prerequisite testing for this you could just move it all into the build-main rule body and do the globbing/etc. in the shell.

Alternatively alternatively, you could use eval to forcibly evaluate the patsubst in the build-main rule I believe (I'd have to test that to be sure the timing works out correctly with how, GNU make at least, hoists make directives in rule bodies).

This version of the idea above for the simple test case works for me:

-include inc.mk

$(warning A:$A)

bar:
        @echo $A

inc.mk:
        touch test1
        echo 'A=$$(wildcard test*)' > '$@'

force: ;

Edited both sample makefiles to include a force rule on the included makefile to force make to build the included file every time. Without that (and without fancy automatic dependency generation/detection) make will only build the included file the first time and then never touch it again. I believe the force, at a cost of always trying to build it, will avoid that problem.

That being said, for this case, MadScientist's answer is probably the better choice.

Upvotes: 3

MadScientist
MadScientist

Reputation: 101081

The simplest solution is to use the shell to compute the value, rather than using make rules. Like this:

EFLAGS += -I$(PWD)/include 
EFLAGS += -pa $(PWD)/ebin
## $(PWD)/deps/* will only have contents after Rebar runs
EFLAGS += $(patsubst %,-pa %,$(wildcard $(PWD)/deps/*/ebin))

build-deps:
        ./rebar get-deps
        ./rebar compile

build-main: build-deps
        for f in $(PWD)/deps/*/ebin; do paflags="$$paflags -pa $$f"; done; \
        erlc $(EFLAGS) $$paflags $(INFILE)

Upvotes: 4

Related Questions