2

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.

Nathaniel Waisbrot
  • 23,261
  • 7
  • 71
  • 99
  • If you use a regular macro (with a new name, EXFLAGS), such as `EXFLAGS = $(patsubst %,-pa %,$(wildcard $(PWD)/deps/*/ebin))` and then list `$(EXFLAGS)` on the command line, I think the macro should be evaluated when used, so the `patsubst` should not occur until after the directory is populated. At least, it's a theory... – Jonathan Leffler Jun 03 '14 at 16:04

2 Answers2

4

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)
MadScientist
  • 92,819
  • 9
  • 109
  • 136
3

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.

Community
  • 1
  • 1
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • No, this appears to perform the `wildcard` substitution before the lines above, so the generated file is missing the values I want. – Nathaniel Waisbrot Jun 03 '14 at 16:21
  • Oh, was make evaluating that `patsubst`? Oops, yeah, you need to escape that so it ends up *in* the `paflags.mk` file. Editing. – Etan Reisner Jun 03 '14 at 16:25
  • Writing out a file to include is neat, but it just gets me back where I started. The first run (where the files don't exist) the include file is missing and so `EFLAGS` is incomplete. On subsequent runs, the include file always holds data from the *prior* run. – Nathaniel Waisbrot Jun 03 '14 at 16:32
  • When make can't find the include file it creates it and restarts. After the restart the file should already exist, attempt to get recreated (which should cause no change) and the files should exist when it tries to evaluate the include files contents. Is that not happening? Can you show me what is happening (with `$(info)` and `make -d` perhaps)? – Etan Reisner Jun 03 '14 at 16:34
  • 1
    This method (include auto-rebuild) will work as well, if you get it right. It's good if you really need that variable to be defined outside of the `build-main` rule. But if you just need it in that one place it's most straightforward to use the shell to compute it. – MadScientist Jun 03 '14 at 16:42
  • But, err, you definitely do _not_ want to make the makefile `.PHONY`. I think you may get infinite re-exec if you do that (I'm not sure about this but it seems reasonable) since the included file will never be up to date. The problem with this method is, how do you know when to rebuild `paflags.mk`? You'd need to list files that might cause the output to change as prerequisites, otherwise it will have old data. There's a lot of complexity to getting this right. – MadScientist Jun 03 '14 at 16:44
  • @MadScientist Hah, so you don't get infinite recursion you just don't get any recursion. And yes, I tried .PHONY to avoid needing to get that right (like the autotool advanced mechanism on your site needs to do). – Etan Reisner Jun 03 '14 at 16:45
  • See comment above: if `paflags.mk` is phony then it's out of date, so make rebuilds it and re-execs. Then it checks `paflags.mk` and it's phony so out of date, so make rebuilds it and re-execs. Then it checks `paflags.mk` and it's phony so out of date, so make rebuilds it and re-execs. Then it checks ... you get the idea :) – MadScientist Jun 03 '14 at 16:47
  • You might get that on newer make versions but the 3.81 I have here just stops after the first build saying 'Updating goal targets.... Pruning file `inc.mk'. make: Nothing to be done for `inc.mk'.' – Etan Reisner Jun 03 '14 at 16:48
  • Whoah, that is some cool stuff! The prior version wasn't restarting make for me, but now I get it. Thanks for showing me this @EtanReisner. – Nathaniel Waisbrot Jun 03 '14 at 16:51
  • Etan, your sample makefile has the rule for `inc.mk` first, so when you run `make` that's what it will build :). Either move `bar` to be the first rule or run `make bar`. That said, I can't get the infinite recursion to happen. It looks like there's a special check in make where if we've already recursed at least once, ignore .PHONY or something. – MadScientist Jun 03 '14 at 16:55