tags:

views:

724

answers:

6

In a makefile, I define a variable using the define directive. This variable will hold a configurable list of commands that I want to execute.

I would like this variable to get a list of files (.foo files, for example). These files are created during the makefile execution. For example makefile:

MY_VAR = $(wildcard *.foo)
define MY_VAR2
    echo $(1) $(MY_VAR)
endef

foo: create_files
    $(call MY_VAR2, ls)
    rm -f *.foo

create_files:
    touch foo.foo
    touch bar.foo

I do not get the desired results. It appears that MY_VAR2 is evaluated upon declaration.

Is there a way to get the desired behavior?


edit:

The $(shell) command, as sateesh correctly pointed out, works for the example above. However, it does not work for the example below. The main difference in this example is that the new files are created inside MY_VAR2.

MY_VAR = $(wildcard *.foo) 
TEST_VAR = $(shell ls *.foo)

define MY_VAR2 
    @touch foo.foo
    @touch bar.foo
    @echo "MY_VAR" $(1) $(MY_VAR)
    @echo "TEST_VAR" $(1) $(TEST_VAR)
endef 

foo:
    $(call MY_VAR2, ls) 
    @rm -f *.foo 

I can solve the above by adding rules. Is there a simpler method?

A: 

Could you try the filter command? You can find an example here.

kgiannakakis
+1  A: 

4.4.3 The Function wildcard

Wildcard expansion happens automatically in rules. But wildcard expansion does not normally take place when a variable is set, or inside the arguments of a function. If you want to do wildcard expansion in such places, you need to use the wildcard function

You may work your away around the eval function.

These created files should be dependencies of another rule so that the dependency graph is correct.

References:

Gregory Pakosz
I didn't know make had and `eval` function. Nice to know. I'll play around with it (and rtfm).
Gilad Naor
Well indeed, i could have pointed `$(shell)` as @sateesh did, but somehow I wasn't sure your example was REALLY what you wanted to do (I thought it was just a dummy example). In general, dynamic makefile constructs are achieved through `$(eval)`
Gregory Pakosz
Yes, it is a dummy example. I am still not 100% sure that `$(shell)` will fit my purposes.
Gilad Naor
+1  A: 
sateesh
This looks promising. I was having my doubts until I reached the wording in bold :-). I'll be playing around with it...
Gilad Naor
A: 

How about using include?
The point is that the make needs two pass of execution.
First, you need to let make create what it wants,
Then you'll let make to parse the generated rule in the second pass after the execution of the first rule.

define MY_VAR2
    echo $(1) $(MY_VAR)
    $(1) $(MY_VAR)
endef

-include .listoffiles

foo: create_files
    $(call MY_VAR2, ls -al)
    rm -f *.foo .listoffiles

create_files: .listoffiles

.listoffiles:
    touch foo.foo
    touch bar.foo
    @echo "MY_VAR = \$$(wildcard *.foo)" > $@

It just does what you want, I suppose.
Two pass evaluation doesn't mean you have to type make twice.
It will be done by make automagically.

% make -f test.mk
touch foo.foo
touch bar.foo
echo  ls -al bar.foo foo.foo
ls -al bar.foo foo.foo
ls -al bar.foo foo.foo
-rw-rw-r-- 1 yo-hei clearusers 0 Jan  8 08:44 bar.foo
-rw-rw-r-- 1 yo-hei clearusers 0 Jan  8 08:44 foo.foo
rm -f *.foo .listoffiles
holmes
I actually really abuse this kind of functionality (using eval too!) of make to generate rules on the fly with least data and rule template to manage at the core of the build system.gnu make is quite clean and fast in this way. you don't need python, ruby, ocaml for this.... you'll get -j parallelism for free, quite useful string manipulation methods, but I wish having a little bit more of candy-ish functionality in gnu make...
holmes
+2  A: 

It looks to me like you are abusing make, trying to write a shell script in make.

If you write a shell script, write a shell script. You execute your commands in sequence, and you are able to know what files are present when executing each line.

touch foo.foo
touch bar.foo
your-command `ls *.foo`

On the other side, if you want to make usage of make, then have rules and dependencies, you won't even have to use define if you go the make way of thinking.

foo: create_files
    your-command $(wildcard *.foo)
    rm -f *.foo

create_files:
    touch foo.foo
    touch bar.foo
Didier Trosset
I may very well be abusing make. I'll try and explain what I want to do. I want to perform some actions before and after resolving some rules. In the second example you give, I would like the ***body*** of `foo` to execute when resolving a third (and 4'th...) rule. My thinking was that `define` is pretty much a function declaration.I'll think about it. Maybe a shell script is the best route, though the situation is perhaps a bit more complicated.
Gilad Naor
A: 

What about doing your expansion in the shell instead of in make?

MY_VAR = $$(ls *.foo)
define MY_VAR2
    @echo $(1) $(MY_VAR)
endef

foo: create_files
    $(call MY_VAR2, ls)
    @rm -f *.foo

create_files:
    @touch foo.foo
    @touch bar.foo

The double-$ will allow the shell to expand the file search part. I can't help but agree with the other posts about there being a more elegant way to do this, but this is an option for you.

Works for your second example too:

MY_VAR = $(wildcard *.foo) 
TEST_VAR = $$(ls *.foo)

define MY_VAR2 
    @touch foo.foo
    @touch bar.foo
    @echo "MY_VAR" $(1) $(MY_VAR)
    @echo "TEST_VAR" $(1) $(TEST_VAR)
endef 

foo:
    $(call MY_VAR2, ls) 
    @rm -f *.foo
Rob Curtis