.EXPORT_ALL_VARIABLES works only when made 'phony' - makefile

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.

Related

Makefile default rule pattern

Just trying to write a conditional Makefile using this skeleton:
TARGET = test
ifeq ($(FOO),y)
$(TARGET):
#echo This is test
$(TARGET)-a:
#echo This is test-a
$(TARGET)-b:
#echo This is test-b
else
$(info FOO is disabled)
endif
When the FOO condition is true, the set of rules based on the TARGET variable (composed of one $(TARGET) and a set of $(TARGET)-substring) work as expected:
$ make test
This is test
$ make test-a
This is test-a
When the FOO condition is false, I want to define a default rule for all my targets, just to report on the screen FOO variable is disabled. I don't know the proper way to do that. Tried some options:
Option1, using the skeleton example, the string "FOO is disabled" is always printed, but it generates an error:
$ make test-a
FOO is disabled
make: *** No rule to make target 'test-a'. Stop.
$ make test
FOO is disabled
make: *** No rule to make target 'test'. Stop.
Option 2, if try to modify the false rule in this way:
else
$(TARGET)-%:
$(info FOO is disabled)
endif
Then all $(TARGET)-substring targets work as expected:
$ make test-a
FOO is disabled
make: 'test-a' is up to date.
$ make test-b
FOO is disabled
make: 'test-b' is up to date.
But this rule fails when making $(TARGET):
$ make test
make: *** No rule to make target 'test'. Stop.
Option 3, if try to remove the hyphen on the false rule defined in option2:
else
$(TARGET)%:
$(info FOO is disabled)
endif
then making $(TARGET) executes a default rule for compiling a test.o object file:
$ make test
FOO is disabled
cc test.o -o test
cc: error: test.o: No such file or directory
cc: fatal error: no input files
compilation terminated.
make: *** [<builtin>: test] Error 1
And I am becoming a little bit crazy trying so satisfy this default rule. Please some help with this would be very useful. Tnks!
There are several ways to solve this, but the simplest is probably just to add another rule:
else
$(TARGET):
#echo FOO is disabled
$(TARGET)%:
#echo FOO is disabled
endif
(I changed $(info ... to #echo ... because the latter will run only when Make execute the rule, while the former will run if the conditional defines those rules, even if the target is something else.)
EDIT: Yes, it's possible to solve this with only one rule, there is more than one way, but no perfect way.
Here is one way:
TARGET = tes
...
else
$(TARGET)%:
#echo FOO is disabled
endif
Note that the last character of test has been removed. The good news is that this rule will apply to test, test-a and test-b; the bad news is that it will also apply to tesw.
As #MadScientist says, .DEFAULT rule fixes the issue when you have a single Makefile. So this would be the final Makefile:
TARGET = test
ifeq ($(FOO),y)
$(TARGET):
#echo This is test
$(TARGET)-a:
#echo This is test-a
$(TARGET)-b:
#echo This is test-b
endif
.DEFAULT:
#echo This is the default rule
Thanks a lot for your help!

.ONESHELL target (a 'phony' target) has no effect on makefile

After establishing that prerequisites to .PHONY are made target-like.
And looking at the docs, where the following special targets seem to follow the same syntax rules:
'.EXPORT_ALL_VARIABLES'
Simply by being mentioned as a target...
...
...
...
'.ONESHELL'
If '.ONESHELL' is mentioned as a target...
I tried to following makefile:
all:
#foo=bar
#echo "foo=$${foo}"
.PHONY: all
.PHONY: .ONESHELL
By running it, and got:
foo=
Which definitely is not a result from "oneshell" execution.
So, are some special variables more special than others, regarding their syntax rules?
.ONESHELL should be provided as the target not as the prerequisite as you have specified in your question. If you specify .ONESHELL: all you should get the expected output of foo=bar. That is what I get when running make on the following makefile.
.ONESHELL: all
.PHONY: all
all:
#foo=bar
#echo "foo=$${foo}"

Using Make with force snippet to override other file

I've been trying to get a makefile, a, to include another makefile, b, if the target specified is not found in file a. I'm using this snippet to try and achieve this, but from echos I've put into the file I can see that makefile b is being accessed even when the target is found in a and run.
The snippet I'm using from the link above is:
foo:
frobnicate > foo
%: force
#echo "No target found locally, running default makefile"
#$(MAKE) -f Makefile $#
force: ;
Specifically I'm getting "Nothing to be done" outputs when makefile b is being used, and makefile a is behaving as expected. This is shown below:
$ make all # all target appears in both make files
No target found locally, running default makefile
make[1]: Entering directory `/home/user/currdir' # (b)
make[1]: Nothing to be done for `Makefile'.
make[1]: Leaving directory `/home/user/currdir'
Local all # (a)
Is there a better way to be doing this?
addition: After adding another echo to the % rule, I've found that $# is "Makefile", when it should be the target trying to be built.
I don't really understand your question based on the example you gave; there is no "a" or "b" in that example, just one Makefile.
However, the behavior you're seeing is due to GNU make's re-making makefiles capability. When you create match-anything pattern rules as you've done, you have to consider that every single target or prerequisite that make wants to build will match that rule. That's a large burden.
You can avoid having remade makefiles match by creating explicit rules for them, such as:
Makefile: ;

How can I set long options within a makefile

I am writing a makefile for distribution among students. To ease up their hacking experience, I would like make to warn about uninitialised variables.
I know there is the option --warn-undefined-variables to do just this, and of course, I can add an alias รก la alias make="make --warn-undefined-variables" to my .bashrc. But I would like to set this option within the makefile so students will automatically profit from those warnings too, when they start to extend the makefile.
The logical way to do so would be the MAKEFLAGS variable. However, while it works for short options, I cannot get it to work with --warn-undefined-variables as described in Can make warn me, when I use unset variables?
Makefile:
MAKEFLAGS=--warn-undefined-variables
$(info MAKEFLAGS: $(MAKEFLAGS))
$(info ${BAR})
Call:
$ make
MAKEFLAGS: --warn-undefined-variables
make: *** No targets. Stop.
$ make --warn-undefined-variables
MAKEFLAGS: --warn-undefined-variables
Makefile:3: warning: undefined variable 'BAR'
make: *** No targets. Stop.
When I change the MAKEFLAGS to -d the console is flooded with debug information, so I know MAKEFLAGS is set correctly. Any suggestions?
I have GNU make 4.0 here and I cannot for the life of me get make to honor MAKEFLAGS= --warn-undefined-variables with a straightforward Makefile. However, if I make the Makefile invoke itself, then MAKEFLAGS= --warn-undefined-variables works in the child invocation!
MAKEFLAGS= --warn-undefined-variables
$(info MAKEFLAGS: $(MAKEFLAGS))
$(info $(BAR))
# This prevents a warning if we invoke make without a target...
MAKECMDGOALS?=
all:
ifndef RECURSED
$(MAKE) RECURSED=1 $(MAKECMDGOALS)
else
echo $(FOO)
endif
If I just run make, I get:
MAKEFLAGS: --warn-undefined-variables
make RECURSED=1
make[1]: Entering directory '/tmp/t1'
MAKEFLAGS: --warn-undefined-variables
Makefile:3: warning: undefined variable 'BAR'
Makefile:12: warning: undefined variable 'FOO'
echo
make[1]: Leaving directory '/tmp/t1'
Either I'm borking on something... or there's a bug in make. I'm inclined to think the latter.

using the same make file in a separate directory

I have a makefile in directory foo and would like to use the same makefile in a subdirectory bar. I have been doing the following:
all:
<do work in foo>
cd bar;
make -f ../Makefile <target to make in bar>
This gets very messy when I try to do target specific variable values as I need to pass them on the command line when calling make in bar. Is there a cleaner way to do this?
I cannot tell from the question whether the following solution suites your needs, it might - or might not - work for you.
If your situation is that you simply want the same Makefile features available, include could be a solution. You can create a Makefile in directory bar in which you do everything you need specific to bar, and besides that, you do:
include ../foo/Makefile
Caveat! This doesn't work straight-forward. There cannot be two recipes with the same name. For example, if you want foo/Makefile to do recipeBar for all, and you want foo/Makefile to do recipeFoo and recipeBar for all, the following does not work:
foo/Makefile:
.PHONY: all
all:
recipeFoo
bar/Makefile:
.PHONY: all
all:
reciveBar
include foo/Makefile
Instead, the recipes have to be separated into unique names. However, dependency rules can be there multiple times, so it's not really a challenge to workaround this caveat. So, the following would work:
foo/Makefile:
.PHONY: all
all: allFoo
.PHONY: allFoo
allFoo:
recipeFoo
bar/Makefile:
.PHONY: all
all: allBar
.PHONY: allBar
allBar:
recipeBar
include foo/Makefile
Now, if you run make in bar, it would run recipeFoo and recipeBar.
If the sequence matters to you and recipeFoo must run before recipeBar, make allBar dependent on allFoo, like this:
bar/Makefile:
.PHONY: all
all: allBar
.PHONY: allBar
allBar: allFoo
recipeBar
include foo/Makefile
If you want your target-specific variables available when you call another make (for which I recommend to use $(MAKE) not make), you can export your variables - with the corresponding consequences (environment space overflow risk on some Windows versions, .
For example, if you have a target-specific variable FOO for target all in Makefile, and you want that when calling Submake.mak that variable is known, it works like this:
Makefile:
all: export FOO:=bar
.PHONY: all
all:
$(MAKE) -f Submake.mak
Submake.mak:
.PHONY: all
all:
echo $(FOO)
Create a link (hard or symbolic, your choice) in bar to ../Makefile. Then, as Carl points out in his comment, you can make -C bar and everything should work. (As of gmake 3.81, at least, make switches to the new directory first, then does its thing. I cannot speak for gmake 4.0.)

Resources