make variable name cannot contain +? - makefile

It seems that makefile variable cannot contain +. My environment: Ubuntu 14 LTS, GNU Make 3.81, bash.
G++=g++ -std=c++11
test:
$(G++) a.cpp -o a
test1:
g++ -std=c++11 a.cpp -o a
make test1 successes, while make test failes with error
make: aa.cpp: Command not found
I find the following in "GNU make" book.
A variable name may be any sequence of characters not containing ‘:’, ‘#’, ‘=’, or white-
space. However, variable names containing characters other than letters, numbers, and
underscores should be considered carefully, as in some shells they cannot be passed through
the environment to a sub-make

No, that works ok. Your problem is a make parsing error.
G++=echo
B++:=echo
test:
$(G++) a.cpp -o a
test1:
echo a.cpp -o a
test2:
$(G+) a.cpp -o a
test3:
$(B++) a.cpp -o a
Run:
$ make test
a.cpp -o a
make: a.cpp: Command not found
make: *** [test] Error 127
$ make test1
echo a.cpp -o a
a.cpp -o a
$ make test2
echo a.cpp -o a
a.cpp -o a
$ make test3
echo a.cpp -o a
a.cpp -o a
The issue is that make is seeing G++=echo as G+ += echo and not G++ = echo. The fix is either to use G++:=echo to force the parsing to see := or add a space G++ =echo.

Related

How to make multiple targets by the same rule using target-dependent compilers?

Suppose that I would like to verify the compatibility of hello.c with multiple compilers. How to do it using a Makefile?
Here is a Makefile I write for this purpose.
# Makefile, version 1.
# It tests hello.c using multiple compilers.
TEST = test_gcc test_clang
.PHONY: $(TEST) all
all: $(TEST)
test_gcc: CC = gcc
test_clang: CC = clang
$(TEST): hello
./hello
rm -f hello
hello: hello.c
$(CC) hello.c -o hello
If I run make test_gcc or test_clang, everything works. However, make all leads to the following.
./hello
Hello, world!
rm -f hello
touch hello.c
./hello
make : ./hello : command not found
make: *** [Makefile:10 : test_clang] Error 127
So hello is not remade for test_clang. This seems to a Makfile beginner like me.
Question: In my Makefile, test_clang depends on hello, which has been removed when test_gcc is finished. So why doesn't make generate it again before running ./hello ?
My Attempts:
To solve the problem, I tried the following modification, which touches hello.c after making test_gcc or test_clang. It still does not work, the problem being the same.
# Makefile, version 2.
# It tests hello.c using multiple compilers.
TEST = test_gcc test_clang
.PHONY: $(TEST) all
all: $(TEST)
test_gcc: CC = gcc
test_clang: CC = clang
$(TEST): hello hello.c
./hello
rm -f hello
touch hello.c
hello: hello.c
$(CC) hello.c -o hello
Following the advice of #HolyBlackCat, I tried also the following.
# Makefile, version 3.
# It tests hello.c using multiple compilers.
TEST = test_gcc test_clang
.PHONY: $(TEST) all
all: $(TEST)
test_gcc: CC = gcc
test_clang: CC = clang
$(TEST): hello_$(CC)
./hello_$(CC)
rm -f hello_$(CC)
hello_$(CC): hello.c
$(CC) hello.c -o hello_$(CC)
The output of make all is
gcc hello.c -o hello_gcc
./hello_gcc
Hello, world!
rm -f hello_gcc
./hello_clang
make : ./hello_clang : commande not found
make: *** [Makefile:13 : test_clang] Error 127
This is even stranger --- hello_clang is never made even though it is required (only) by test_clang.
For your convenience, here is the standard hello.c I used for the test.
/* hello.c */
#include <stdio.h>
int main()
{
printf("Hello, world!\n");
return 0;
}
Thank you very much for any suggestions. It will also be appreciated if you comment on my Makefiles in general, not necessarily regarding the question I raised. I am really a beginner.
See the documentation for target-specific variables: it's quite clear that target-specific variables take effect only in recipes. You cannot use them in prerequisites, or of course when defining targets.
It's probably simpler to do this without target-specific variables and just use pattern rules instead:
TEST = test_gcc test_clang
.PHONY: all
all: $(TEST)
test_%: hello_%
./$<
hello_%: hello.c
$* $< -o $#
(I don't know why you're removing the binary immediately after you test it, in this version, so I removed that).

Why does make insist that the files are up to date

Consider this simple example of a Makefile on FreeBSD.
all: hello
hello: hello.o
gcc -o hello hello.o
hello.o: hello.c
gcc -c hello.c
clean:
rm hello.o hello
And whatever I do, change hello.c, or even if I change the content in the Makefile to complete nonsense, make says:
`makefile' is up to date.
What could be an explanation whats going on there?
I guess you have small mess with makefiles. Please note that (for GNU Make): By default, when make looks for the makefile, it tries the following names, in order: GNUmakefile, makefile and Makefile. - make sure you haven't created GNUmakefile`
When it comes to FreeBSD based make it will be: If no -f makefile makefile option is given, make will try to open 'makefile' then 'Makefile' in order to find the specifications.
The only case, I can imagine, follows:
> cat Makefile
all: hello
hello: hello.o
cc -o hello hello.o
hello.o: hello.c
cc -c hello.c
clean:
rm hello.o hello
> make
cc -c hello.c
cc -o hello hello.o
> ./hello
Hello world!
> make clean
rm hello.o hello
> touch makefile
> echo "makefile:" > .depend
> make
`makefile' is up to date.

Makefile not processing all commands

I am trying to convert a hello.c to hello stepwise by first preprocessing then compiling, then assembling then linking. However only the first command of my makefile is being executed:
Osheens-MacBook-Air-1115:Assignment0.1 osheensachdev$ ls
hello.c makefile
Osheens-MacBook-Air-1115:Assignment0.1 osheensachdev$ cat makefile
hello.i : hello.c
gcc -E hello.c -o hello.i
hello.s : hello.i
gcc -S hello.i -o hello.s
hello.o : hello.s
gcc -c hello.s -o hello.o
hello : hello.o
$ld hello.o -e main hello
clean:
rm hello.i hello.s hello.o hello
Osheens-MacBook-Air-1115:Assignment0.1 osheensachdev$ make
gcc -E hello.c -o hello.i
Osheens-MacBook-Air-1115:Assignment0.1 osheensachdev$ make
make: `hello.i' is up to date.
I've searched online about chained files and didn't find anything specific about why this shouldn't work.
I've also specified the target: [dependancies] so I don't see why this shouldn't work.
The first target of a Makefile is the default target.
This is why you see in many Makefiles a all target at the top which is intended to "make everything":
all: build test
build: <prerequisites>
test: <prerequisites>
Since you didn't specify one, Make builds hello.i only (plus everything needed to build this target):
hello.i doesn't exist yet.
hello.i needs hello.c. Make hello.c first.
hello.c is "made" already. Nothing to do.
Now make hello.i i.e., run gcc -E hello.c -o hello.i.
Stop
Then you ran Make again (without any specific target)
hello.i exists already. Nothing to do.
Stop.
I suspect that you wanted to make "hello", i.e., your program.
Either:
Move "hello" at the top and make it your default target
Or run make hello

why make behaves different with quotes?

I know quotes are not supposed to be used within Makefile, but just out of curiosity, why make behaves differently with make foobar and make. See detailed code below.
Makefile:
TARGET = 'foobar'
$(TARGET): foobar.cpp
g++ -g $^ -o $#
clean:
rm foobar
output:
$ make
g++ -g foobar.cpp -o 'foobar' <-- correct
$ make clean
rm foobar
$ make foobar
g++ foobar.cpp -o foobar <-- incorrect but works. Why?
$ make clean
rm foobar
$ make baz <-- doesn't work, which is normal
make: *** No rule to make target 'baz'. Stop.
$
This only "works" because the shell is stripping the single quotes from your first example for you.
The quotes are literally in the value of the $(TARGET) make variable. make doesn't dequote the right-hand side of the TARGET = 'foobar' assignment.
You can see this by using $(info $(TARGET)) in your makefile.
So your target line:
$(TARGET): foobar.cpp
is creating a target with the name 'foobar' and not foobar like you expect.
This is why running make does the "right" thing and make foobar does something else.
make foobar is running the make built in rule for %: %.cpp.
The fact that your default 'foobar' target works to create foobar is because the shell sees the single quotes and strips them.
You'll notice that if you make make; make make will build your 'foobar' target twice but make foobar; make foobar will tell you there is nothing to be done the second time. That's because the first target creates a file different from what make is expecting.
If you were to quote $# in your recipe line you would see different behavior.
$(TARGET): foobar.cpp
g++ -g $^ -o '$#'
for example would have make run g++ -g foobar.cpp -o ''foobar'' and generate a foobar file while
$(TARGET): foobar.cpp
g++ -g $^ -o "$#"
would have make run g++ -g foobar.cpp -o "'foobar'" and generate a 'foobar' file (which would cause make; make to report nothing to be done for the second make run).
You want the quotes in the recipe line not in the variable here.
TARGET = foobar
$(TARGET): foobar.cpp
g++ -g $^ -o '$#'
clean:
rm foobar
That being said since you can't have spaces in make target names (not reliably at least) the need for those single quotes (or any quoting) is diminished since you only need it if the filename contains shell metacharacters.
It's because make uses a default rule when it does not find specific rules to build a target.
You can compile program from program.cpp even without or with an empty Makefile. Try
make -f /dev/null foobar
The default rules are specified by POSIX and your make implementation has probably some of its own.
Trying to build baz fails, because none of the default rules knows how to build a baz.c or baz.cpp or any of the other built-in source files that could be used to build baz.

How to restore g++/gcc compiling messages

I'm using CentOS 6.5. When I do a make, I typically see the full gcc/g++ commands that the Makefile is executing, like
...
gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I/opt/emacs/emacs-24.3/lib -I../src -I/opt/emacs/emacs-24.3/src -g3 -O2 -MT pthread_sigmask.o -MD -MP -MF .deps/pthread_sigmask.Tpo -c -o pthread_sigmask.o pthread_sigmask.c
...
But in some systems, I only see:
$ make
Building test1.o...
Building test2.o...
...
Is it possible to change the "Building ..." messages back to the full gcc/g++ command output?
The output that you see when you run make with a given makefile
depends on how the makefile is written. You will see the
output that the author of the makefile wants you to see.
If a command in a recipe in the makefile is prefixed with #,
then make will not echo the command. So if my makefile is, e.g.
foobar: foobar.o
gcc -o $# $<
foobar.o: foobar.c
gcc -c -o $# $<
then the output of make will be:
gcc -c -o foobar.o foobar.c
gcc -o foobar foobar.o
But if I change the makefile to:
foobar: foobar.o
#echo "Linking foobar"
#gcc -o $# $<
foobar.o: foobar.c
#echo "Compiling foobar"
#gcc -c -o $# $<
then the output becomes:
Compiling foobar
Linking foobar
So to see the output that you would prefer to see you will have to edit the
makefile, removing the #-prefixes from the commands you expect to see
and deleting entirely the commands that print the "Building..." messages.
At least, this is what you would need to do if the makefiles that bother
you in this way build the target using recipes that directly invoke gcc/g++. It
is possible that they build their targets using recipes that invoke some intermediate
tool that doesn't echo the compiler commands and instead emits the "Building..."
messages. Without seeing the makefile(s) I can't say.

Resources