Reputation: 377
Say in the working directory, I have:
$ find . | grep testfile
./testfile1
This is my Makefile:
list_files:
@echo "Show Files..."
@echo $(shell find . | grep testfile)
@touch testfile2
@echo $(shell find . | grep testfile)
@rm testfile2
With make
, I got this:
$ make list_files
Show Files...
./testfile1
./testfile1
Why this happened? I expected it to be something like this:
Show Files...
./testfile1
./testfile1 ./testfile2
I have found an explanation from this answer that is pretty close to the truth:
The reason your attempt doesn't work is that make will evaluate all lines of the recipe before it starts the first line.
But there is no references provided there, I just cannot convinced myself of this working mechanism of GNU Make.
Could anyone give some clues? Thanks!
Upvotes: 2
Views: 1295
Reputation: 180715
Why this happened?
Because, with one caveat that does not apply to your case, make
functions such as $(shell ...)
are evaluated when the makefile is parsed, not during the execution of recipes.
Why all the variables/function in recipes inside a rule, are expanded likely simultaneously after target is invoked?
They're not. They are expanded before the target's recipe runs. In fact, before make
even determines whether the recipe should be run.
But there is no references provided
This is covered in the manual. See in particular section 8.14, The shell
function:
The commands run by calls to the
shell
function are run when the function calls are expanded (see How make Reads a Makefile).
... which refers to section 3.7, How make
Reads a Makefile, in particular:
GNU
make
does its work in two distinct phases. During the first phase it reads all the makefiles, included makefiles, etc. and internalizes all the variables and their values and implicit and explicit rules, and builds a dependency graph of all the targets and their prerequisites. During the second phase,make
uses this internalized data to determine which targets need to be updated and run the recipes necessary to update them.
It also relies on section 8.1, Function Call Syntax:
A function call resembles a variable reference. It can appear anywhere a variable reference can appear, and it is expanded using the same rules as variable references.
"The same rules" of course includes the rules for when expansions are performed.
Your recipes should be written in the language of the target shell, usually /bin/sh
. All make
functions and variable references in each recipe will be expanded before any recipe runs, so their expansions cannot reflect the results of running any recipe during the current make
run. It's particularly peculiar to try to use the $(shell ...)
function to do that, because you can just use shell code directly in a recipe.
Upvotes: 1
Reputation: 953
As explained in the comments, all $(shell ...) and other functions are executed (expanded) before executing any lines from the recipe. But you can delay the expansion:
list_files:
@echo "Show Files..."
@echo $$(shell find . | grep testfile)
@touch testfile2
@echo $$(shell find . | grep testfile)
@rm testfile2
This yields the expected output. Note the double $$. Make will still expand all variables/functions first before executing the recipe, and remove one of the dollar signs. The expressions will be expanded again, when executing the recipe lines.
Of course, you're better off without the $(shell) in this example. However, it's not inherently a bad idea. Sometimes you need to execute shell commands before expanding other variables, and then $(shell) is the trivial solution.
Upvotes: 1