开发者

Makefile define: recursive expansion question

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_VA开发者_StackOverflow中文版R2.

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?


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


I'm presuming here that you are using here GNU Make. I think you can get the desired
result using the "shell" built-in function provided with GNU Make.

Below is the make file snippet that demonstrates how you can get the result:

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

define MY_VAR2 
    @echo "MY_VAR" $(1) $(MY_VAR)
    @echo "TEST_VAR" $(1) $(TEST_VAR)
endef 

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

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

The result of running the above make file for the target "foo" is:

MY_VAR ls
TEST_VAR ls bar.foo foo.foo

So as demonstrated usage of shell function gets the desired result.

From the GNU make documentation:

The shell function performs the same function that backquotes (‘‘’) perform in most shells: it does command expansion. This means that it takes as an argument a shell command and evaluates to the output of the command. The only processing make does on the result is to convert each newline (or carriage-return / newline pair) to a single space.
...
The commands run by calls to the shell function are run when the function calls are expanded. ... files := $(shell echo .c) sets files to the expansion of ‘.c’. Unless make is using a very strange shell, this has the same result as ‘$(wildcard *.c)’ (as long as at least one ‘.c’ file exists).

So I think using a recursively expanded variable (= form) along with shell function you can get the desired result.


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:

  • 4.4.3 The Function wildcard
  • 8.8 The eval function


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


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


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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜