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
精彩评论