Makefile:
SHELL := /bin/bash
all:
.PHONY: /home/% /home/dhp
/home/%:
#echo do user $* USERRRRRR
plop: /home/dhp
#echo plop
Shell:
# ll /home/
total 4
drwxr-xr-x 3 dhp dhp 4096 Mar 22 08:53 dhp
# make plop
plop
I know the topic have been asked many times, and the answer is in
https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
https://www.gnu.org/software/make/manual/html_node/Special-Targets.html
https://www.gnu.org/software/make/manual/html_node/Chained-Rules.html#Chained-Rules
but I don't understand why in my case USERRRRRR string is not printed. /home/dhp is a direct dep of my target, and, due to .PHONY it should be run even if file exists.
If I have no other choice, I may accept to make /home/%: depend on a very fresh file (which will be always more recent than /home/% by using, for example, the lock of PID file of current process, or depend on a virtual target which will never create anny file), but I would consider that as dirty. I don't think .INTERMEDIATE can help here.
EDIT: more tests:
SHELL := /bin/bash
all:
.PHONY: FORCE
.PHONY: /home/%
.PHONY: /home/dhp
FORCE:
/home/%: FORCE
#echo do user $* USERRRRRR
plop: /home/dhp
#echo plop
=>
# make plop
plop
...
SHELL := /bin/bash
all:
.PHONY: FORCE
FORCE:
/home/%: FORCE
#echo do user $* USERRRRRR
plop: /home/dhp
#echo plop
=>
# make plop
do user dhp USERRRRRR
plop
so, making my target phony prevents from running it ? This is strictly the opposite of what the documentation states:
When it is time to consider such a target, make will run its recipe
unconditionally, regardless of whether a file with that name exists or
what its last-modification time is.
Related
I would like all my recipes to be executed from a specific directory, the directory where the Makefile is located.
This is the default behaviour when invoking make without options, but an user could always run :
(cd /somewhere; make -f /path/to/directory/Makefile)
To ensure that make working directory is the same as the directory where the Makefile is located, there are multiple solutions :
run make without options (default), from this specific directory (cd /path/to/directory; make)
use make -C /path/to/directory
cd to /path/to/directory for each recipe, like this :
MAKEFILE_DIR_LOCATION := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
a:
cd ${MAKEFILE_DIR_LOCATION} && do_something_from_makefile_folder
b:
cd ${MAKEFILE_DIR_LOCATION} && do_another_thing_from_makefile_folder
The problem is that the first two solutions requires actions from the user invoking the Makefile, while the last one clutters the Makefile.
Is there a prettier way to ensure that all recipes are executed from the directory where the Makefile is located?
Extra solution (does not work)
I also thought comparing the working directory ($(shell pwd)) to ${MAKEFILE_DIR_LOCATION}, and exit if it does not match (at least to warn the user that make is not correctly invoked), but I can't find how to do this. I tried :
MAKEFILE_DIR_LOCATION := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
WORKING_DIR := $(shell pwd)
ifneq (${MAKEFILE_DIR_LOCATION}, ${WORKING_DIR})
#error "Please run make from the directory of the Makefile, or use make -C"
endif
a:
do_something_from_makefile_folder
b:
do_another_thing_from_makefile_folder
But I got a missing separator error (line #error), or a recipe commences before first target if #error line is indented.
Answering the question you asked without commenting on whether it's a good idea or not, I'm not sure where you found this syntax:
#error "Please run make from the directory of the Makefile, or use make -C"
but it's definitely wrong. error is a make function, so you want this:
$(error Please run make from the directory of the Makefile, or use make -C)
A variant on your last attempt would re-invoke Make in the correct directory, with the same target:
ifneq (${MAKEFILE_DIR_LOCATION},${WORKING_DIR})
%:
$(MAKE) -C ${MAKEFILE_DIR_LOCATION} $#
.PHONY: %
else
## rest of Makefile rules
endif
I have the following Makefile:
SHELLS = $(call adjust, profile bash zsh)
adjust = $(foreach arg, $(1), $(DIR)/$(DOT)$(arg))
sys:
$(eval DIR = /etc)
$(eval DOT = )
usr:
$(eval DIR = $(wildcard ~))
$(eval DOT = .)
# Installation Recipes {{{1
shell-sys: $(SHELLS) | sys
#echo $(SHELLS)
shell-usr: $(SHELLS) | usr
#echo $(SHELLS)
$(SHELLS): $(DIR)/$(DOT)%: $(wildcard %/*)
#echo $(SHELLS)
Now I run make shell-sys and expect the following result:
$ make shell-sys
/etc/profile /etc/bash /etc/zsh
/etc/profile /etc/bash /etc/zsh
/etc/profile /etc/bash /etc/zsh
/etc/profile /etc/bash /etc/zsh
However what I get is the following:
$ make shell-sys
/profile /bash /zsh
/profile /bash /zsh
/profile /bash /zsh
/etc/profile /etc/bash /etc/zsh
Inside Static Pattern Rules $(DIR) and $(DOT) fail to expand, however in ordinary rules they seem to expand just fine. What am I doing wrong? Is there a better way to achieve what the sys and usr targets do?
Before running any recipes (shell commands),
make will read the entire Makefile.
While it does this it expands the dependency lines.
Recipes themselves are simply stored away as recursively expanded variables.
At the end of this stage make has a dependency graph.
In your case, make expands
shell-sys: $(SHELLS) | sys
$(SHELLS) becomes /profile /bash /zsh given the emptiness of $DIR and $DOT.
You can get some hint about make expanding these empty variables by giving make the --warn command-line parameter.
Thus it is as if you had written:
shell-sys: /profile /bash /zsh | sys
#echo $(SHELLS)
/profile /bash /zsh: /%:
#echo $(SHELLS)
(I doubt you have a folder on your disk called % so I'm assuming the $(wildcard ...) expands to nothing.)
Once it has a dependency graph in its little hands,
make can walk it,
executing the recipes as it goes.
You asked it to build shell-sys,
so it first tries to build /profile.
[Probably. Makefiles that rely on this implicit ordering of dependencies are broken IMHO.]
Make now builds /profile.
It knows how to do this, you told it earlier.
It expands the recipe.
#echo $(SHELLS) becomes #echo /profile /bash /zsh.
Make passes echo /profile /bash /zsh to the shell without telling you it has done this due to the # prefix.
echo dutifully prints /profile /bash /zsh.
Similarly for the targets /bash and /zsh.
Next we get to build sys.
Make expands the recipe for sys.
Each separate line in the expansion is passed to a new invocation of the shell.
The expansion however is empty,
so the shell is not called.
As a side effect DIR becomes /etc.
Finally the recipe for shell-sys.
Hopefully you can now see that that expands to #echo /etc/profile /etc/bash /etc/zsh
There's a lot not to like in your formulation,
but the biggest advice I can give is not to cause a $(eval ...) to expand at recipe execution time.
The docs provides:
'.EXPORT_ALL_VARIABLES'
Simply by being mentioned as a target, this tells 'make' to export
all variables to child processes by default. *Note Communicating
Variables to a Sub-'make': Variables/Recursion.
However, the following makefiles show that only by making .EXPORT_ALL_VARIABLES a phony target, then and only then, will it have the desired effect on the makefile, i.e. to export ALL variables.
Makefile(version 1) is:
ifeq "$(MAKELEVEL)" "0"
foo=bar
.DEFAULT:;
all: .EXPORT_ALL_VARIABLES
#$(MAKE)
else
all:
#echo 'foo is: $(foo)'
endif
Running, we get:
make[1]: Entering directory '/home/myname'
foo is:
make[1]: Leaving directory '/home/myname'
Makefile(version 2) is:
ifeq "$(MAKELEVEL)" "0"
foo=bar
.DEFAULT:;
all: .EXPORT_ALL_VARIABLES
#$(MAKE)
# This line is added in THIS version.
.PHONY: .EXPORT_ALL_VARIABLES
else
all:
#echo 'foo is: $(foo)'
endif
Running, we get:
make[1]: Entering directory '/home/myname'
foo is: bar
make[1]: Leaving directory '/home/myname'
Now, the only difference between these 2 versions of makefile, is that in the 2nd version, the .EXPORT_ALL_VARIABLES was made phony.
Why is the 'phoniness' needed in order to work?
Simply by being mentioned as a target,
Why is the 'phoniness' needed in order to work?
It's not. You didn't declare .EXPORT_ALL_VARIABLES as a target, you declared it as a prerequisite:
all: .EXPORT_ALL_VARIABLES
That's a prerequisite, not a target. If you declare it as a target:
.EXPORT_ALL_VARIABLES:
then it will work and you won't have to declare it phony.
A more accurate question would be, why does declaring .EXPORT_ALL_VARIABLES as phony work even though it's not declared as a target? It happens because things that are marked phony are assumed to be targets even if they're not explicitly mentioned as such. That may or may not be a bug, depending on how you interpret the intent of .PHONY.
Your questions recently seem to follow a pattern: read the documentation, then write a makefile that does something similar to but not the same as what the documentation says, observe it doesn't work as described, then ask why not.
I am trying to compile for different software directories with different optimization levels etc. I created the following makefile to do so:
OWNER = betsy molly fred
DOG = poodle mutt doberman
COLOUR = brown red yellow
ATTR = big small
LEGS = 0 3
#we want every possible combination to be excercised
OUTPUT_STUFF = $(foreach own,$(OWNER),$(foreach dog,$(DOG),$(foreach col,$(COLOUR),$(foreach attr,$(ATTR),$(foreach legs,$(LEGS),new/$(own)/$(dog)/$(col)/$(attr)/$(legs)/dogInfo.txt)))))
.PHONY: all
all: $(OUTPUT_STUFF)
define PROGRAM_template
own = $(1)
dog = $(2)
col = $(3)
attr = $(4)
legs = $(5)
BUILD_DIR = new/$(own)/$(dog)/$(col)/$(attr)/$(legs)
#for each build directory, we are going to put a file in it containing the build dir. string
$$(BUILD_DIR)/dogInfo.txt:
#echo "$$#"
mkdir $$(BUILD_DIR)
#echo "$$(BUILD_DIR)" > $$(BUILD_DIR)/dogInfo.txt
endef
#call the function many times
$(foreach own,$(OWNER),$(foreach dog,$(DOG),$(foreach col,$(COLOUR),$(foreach attr,$(ATTR),$(foreach legs,$(LEGS),$(eval $(call PROGRAM_template,$(own),$(dog),$(col),$(attr),$(legs))))))))
As you can see, this simple test program loops through different combinations of owner, dog etc. The end goal is to have a directory, new, that has all owners as dirs, and in those, all dogs, etc. At the bottom is just a file with the path in it.
When I run this, the output is:
new/betsy/poodle/brown/big/0/dogInfo.txt
mkdir new/fred/doberman/yellow/small/3
mkdir: cannot create directory `new/fred/doberman/yellow/small/3': No such file or directory
make: *** [new/betsy/poodle/brown/big/0/dogInfo.txt] Error 1
So, for some reason, the target is ok, but the seemingly exact same variable is the last in my loops. Fundamentally, I don't understand what is happening that well.
Weird foreach + user-defined function behavior in Makefiles seems to answer, but I don't fully get it. In my mind, when the function is called, it fills in all instances with one $, and the escaped ones become $(BUILD_DIR). It then 'pastes' the code to the temporary file, and after it's done all the calls it evaluates the file, substituting the variables as normal.
One (ugly) solution I thought of is to make the BUILD_DIR variable different every time like so:
B_D_$(1)_$(2)_$(3)_$(4)_$(5) = ~~~
Alex is correct (although I think he means recipe, not receipt :-)). The best way to debug complex eval issues is to replace the eval function with a call to info instead. So if you have something like:
$(foreach A,$(STUFF),$(eval $(call func,$A)))
then you can rewrite this as:
$(foreach A,$(STUFF),$(info $(call func,$A)))
Now make will print out to you exactly what the eval is going to parse. It's usually pretty clear, looking at the makefile output, what the problem is. In your case you'll see something like this in the output (leaving out all the extra variable settings):
BUILD_DIR = new/betsy/poodle/brown/big/0
$(BUILD_DIR)/dogInfo.txt:
#echo "$$#"
mkdir $(BUILD_DIR)
#echo "$(BUILD_DIR)" > $(BUILD_DIR)/dogInfo.txt
BUILD_DIR = new/betsy/poodle/brown/big/3
$(BUILD_DIR)/dogInfo.txt:
#echo "$$#"
mkdir $(BUILD_DIR)
#echo "$(BUILD_DIR)" > $(BUILD_DIR)/dogInfo.txt
etc. Notice how you're setting the global variable BUILD_DIR every time. In make, variables have only one value (at a time). While make is reading the makefile it expands the target and prerequisite lists immediately, so whatever value BUILD_DIR has at that time will be used for targets/prerequisites, so this works for you.
But when make finishes reading the makefile, the value of BUILD_DIR will always be the last thing you set it to; in this case new/fred/doberman/yellow/small/3. Now make starts to invoke the recipes for each target, and when it does that it will expand BUILD_DIR in the recipes then, and so ALL the recipes will get that same value.
As Alex points out, you should ensure that your recipe uses only automatic variables like $#, which are set correctly for each rule. If you do that you'll notice that you don't really need to redefine the rule at all because it's actually the same recipe for all the targets. And if you notice THAT, you'll notice you don't need the whole eval or call complexity in the first place.
All you have to do is compute the names of all the targets, then write a single rule:
ALLDOGINFO = $(foreach own,$(OWNER),$(foreach dog,$(DOG),$(foreach col,$(COLOUR),$(foreach attr,$(ATTR),$(foreach legs,$(LEGS),new/$(own)/$(dog)/$(col)/$(attr)/$(legs)/dogInfo.txt)))))
$(ALLDOGINFO):
#echo "$#"
mkdir $(dir $#)
#echo "$(dir $#)" > $#
If you don't want the trailing slash you have to use $(patsubst %/,%,$(dir $#)) instead.
The problem is that when $$(BUILD_DIR) is evaluated in receipt, the loop is already complete. The solution is to rewrite the receipt:
$$(BUILD_DIR)/dogInfo.txt:
#echo "$$#"
mkdir $$(#D)
#echo "$$(#D)" > $$#
I don't think your problem is necessarily with something to do with make. This command:
mkdir new/fred/doberman/yellow/small/3
will fail if one of the parent directories (for example, yellow) doesn't already exist. The error it spits out in this case is the one you're getting, so it seems likely this is the case. If you want a command that makes all parent directories of a given directory as needed, you should run mkdir -p, like this:
mkdir -p $$(BUILD_DIR)
See the mkdir man page for a full description of what -p does.
I want the rules of a target to be executed but all dependent targets shall regard this target as satisfied.
How can I achieve this?
Example:
$(NETWORK_SHARE)/foo.log:
#echo Warning: server offline, still continue ...
#exit 0
foo.csv: $(NETWORK_SHARE)/foo.log
#echo Long export from a (different) server
#echo sleep 20
#echo foo > $#
If $(NETWORK_SHARE)/foo.log exists: foo.csv shall be rebuilt if $(NETWORK_SHARE)/foo.log is newer than foo.csv; otherwise nothing should happen (default)
If $(NETWORK_SHARE)/foo.log does not exist (e.g., server offline, failure, ...) only a message indicating a problem should be printed but foo.csv shall only be built if foo.csv does not exist.
I played around with .PHONY and returning different return values but for case 2, the expensive "export" happens as soon as I execute something on $(NETWORK_SHARE)/foo.log ...
Regards
divB
Looks like instead of using some old file (that someone can accidentally touch), you can use an order-only prerequisite. Here's a quote from the GNU makefile manual (chapter 4.3)
Occasionally, however, you have a situation where you want to impose a
specific ordering on the rules to be invoked without forcing the
target to be updated if one of those rules is executed. In that case,
you want to define order-only prerequisites. Order-only prerequisites
can be specified by placing a pipe symbol (|) in the prerequisites
list: any prerequisites to the left of the pipe symbol are normal; any
prerequisites to the right are order-only:
targets : normal-prerequisites | order-only-prerequisites
Great, thanks to Thiton's answer in my related question (Force make to find out-of-date condition from file) I can now provide a hack to solve this:
.PHONY: always-remake
NETWORK_SHARE = //server/dfs/common/logs
.PHONY: all
all: foo.csv
# file does not exist ...
ifeq "$(wildcard $(NETWORK_SHARE)/foo.log)" ""
old_file: always-remake
#echo Warning: network is not available ....
foo.csv: old_file
#echo Expensive export
#sleep 10
#echo $# > $#
else
foo.csv: $(NETWORK_SHARE)/foo.log
#echo Doing expensive export since log file changed ...
#sleep 10
#echo $# > $#
endif
"old_file" is a dummy file which must exist and should never be newer than any other file (e.g. 1/1/1971, 00:00)
Regards
divB