.ONESHELL target (a 'phony' target) has no effect on makefile - 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}"

Related

Makefile function from make commands, NOT shell commands

Is there any way to create multiline functions out of Makefile commands?
I know we can do something like this to encapsulate a recipe (of shell commands) as a function:
define function
#echo 'First argument: $1'
#echo 'Second argument: $2'
endef
.PHONY test-function
test-function:
$(call function, a, b)
With this, running make test-function will give the output:
First argument: a
Second argument: b
I also know we can use the call directive with one-line macros consisting of make syntax/directives (example taken from here):
pathsearch = $(firstword $(wildcard $(addsuffix /$(1),$(subst :, ,$(PATH)))))
LS := $(call pathsearch,ls)
But let's say I wanted to call a macro made up of multiple make commands, including conditionals. How would I achieve that?
When I run make build-type=API build with the following Makefile:
define check-arguments
ifeq ($1, api)
#echo 'Building API'
else ifeq ($1, service)
#echo 'Building Service'
else
$$(error 'Build type must be API or Service')
endif
endef
.PHONY: build
build:
$(call check-arguments, $(build-type))
#echo 'Starting build'
...
...
I keep getting the error Makefile:13: *** missing separator. Stop..
You can use eval. The GNU Make Manual states:
...it [eval] allows you to define new makefile constructs that are not constant; which are the result of evaluating other variables and functions.
eval will parse ifeq and $(error) as part of the makefile instead of as commands for the recipe.
One thing to keep in mind is that eval parses its input by itself, without regard for the surrounding syntax of the makefile. This means that you cannot use it to define only part of a rule, like in your example:
build:
$(call check-arguments, $(build-type))
If we use $(eval $(call check-arguments, $(build-type))), then eval will parse the expansion of check-arguments by itself and complain because the recipe has no target. (See this answer.) The solution here is to include build: in check-arguments somehow.
While having $(eval) is fine, I would like to recommend a different approach, based on target resolution instead of conditionals, like so:
$ cat Makefile
supported_build_types := api service
.PHONY: build
build: build-$(build-type)
.PHONY: $(addprefix build-,$(supported_build_types))
$(addprefix build-,$(supported_build_types)): build-%:
#echo 'Building $*'
#echo 'Starting build'
.PHONY: build-
build-:
$(error Must provide build-type of: $(supported_build_types))
.PHONY: build-%
build-%:
$(error Unsupported build type: $*. Must be one of: $(supported_build_types))
This can allow easier extensibility and maintenance while keeping away nuisances of $(eval)s, $(call)s and appropriate escaping.
Running supported build types:
$ make build build-type=api
Building api
Starting build
$ make build build-type=service
Building service
Starting build
Invalid build type:
$ make build build-type=foo
Makefile:17: *** Unsupported build type: foo. Must be one of: api service. Stop.
Missing build type:
$ make build
Makefile:13: *** Must provide build-type of: api service. Stop.

How should you use .PHONY in included Makefiles?

Say I have a set of Makefile modules:
# foo.mk
rule1: prereq1
recipe1
and
# bar.mk
rule2: prereq2
recipe2
and a primary Makefile:
# Makefile
include foo.mk
include bar.mk
Should .PHONY: be included in each individual .mk file for the phony targets just in that file, or should there be some acculmulated list that is included only in the primary Makefile?
# foo.mk
TARGETS += rule1
...
# bar.mk
TARGETS += rule2
...
# Makefile
.PHONY: $(TARGETS)
I didn't find anything relevant in the GNU Make docs or similar questions.
The statement
.PHONY: rule1
tells Make that it should not consider "rule1" the name of a file to be built. Suppose you put it in the Makefile. What happens when you run another makefile, either foo.mk or a makefile that includes it?
When you run the rule1 rule, do you want Make to treat it as a PHONY target? If your answer isn't "that depends on which makefile I'm using", then you should have the statement in the makefile that defines the rule.
As with other targets, you can add to .PHONY multiple times cumulatively. So giving each file its own .PHONY is can be a nice way of keeping things compartmentalized.

.EXPORT_ALL_VARIABLES works only when made 'phony'

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.

Percentage used in the Makefile rule that aims at renaming all .c files

I think percentage in Makefile means wildcard. As a try, I test a contrive Makefile that aims at changing any .c file to 'hi'. This is my Makefile rule:
%.c:
mv $# hi
I save the file above to 'Makefile', and then type in the terminal
touch hello.c
make
The terminal says
make: `hello.c' is up to date.
which is certainly not what I wanted. Two naive questions:
a) Why does makefile determine that 'hello.c' is actually "up to date"?
b) How can we enforce the rule to be applied anyway?
You have not defined any dependencies in your rule. The hello.c already exists and none of the dependencies have newer timestamp than hello.c.
There are rules with no dependencies like clean. In this case the file clean does not exist and make tries to create it by executing the rule's set of commands. However, as mentioned in GNU make documentation this will not work in the case where clean file is created. The solution on this is to define clean as "Phony Target".
Additionally the output of the command is file hi and not %.c
The Makefile should look like:
%.hi : %.c
mv $< $#

error using makefile, targets and %

I'm trying to debug the following code:
TESTS=$(shell cat yoursourcefile)
all: $(TESTS)
%: compile_design
compile $#_tb.vhd >> log_file.log
simulate $#
I got this error:
makefile_tb.vhd >> log_file.log
as if makefile is a target
this error disappears when I add a character or more before %:
T%: compile_design
compile $#_tb.vhd >> log_file.log
simulate $#
This works but implies that all my targets starts with "T" which is not always the case.
My questions are:
what's exactly the function of % here ?
How to get rid of this error?
As suggested, I added
makefile: ; $#:
at the end, so I have now:
TESTS=$(shell cat yoursourcefile)
all: $(TESTS)
%: compile_design
compile $#_tb.vhd >> log_file.log
simulate $#
makefile: ; $#:
then when I do:
make all
I get [all] error2 all_tb.vhd >> log_file.log
but all_tb.vhd does not exist !
The %: compile_design rule is a "match-anything" pattern rule. It says "hey make, if you ever want to build any file, with any name, then you can do it by running these commands. Oh and by the way, if you have a file you want to build and it's older than the compile_design file, then you need to rebuild it". Generally you want to avoid match-anything rules, but if your target names truly have no specific pattern, you can't.
When you add the T before it then it tells make that instead of any file, that rule can only build files that begin with T.
The reason make is trying to rebuild the makefile is that GNU make has a special feature that allows it to remake its own makefiles. So after it reads its makefile it will try to re-make it. Normally this has no effect because there's no rule to build a makefile, but here you've added a rule that you've told make can build anything. Adding the T keeps the pattern from matching Makefile because Makefile doesn't begin with T.
The simplest thing for you to do is define an explicit rule for the makefile: make always chooses an explicit rule, if it exists, over an implicit rule like a pattern rule:
Makefile: ; #:
This creates an explicit rule that does nothing (: is the shell built-in command that does nothing).

Resources