Why automatic variable in my makefile doesn't find any target? - makefile

I've written this hello world in hello.c:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
exit( 0 );
}
my Makefile is:
%: %.c
When I run make I will get this error: make: *** No targets. Stop.

Your makefile provides a rule %: %.c specifying that it's extensionless executables and .c files that you're interested in (in fact, just the built-in rules do that much), but gives no hint that there's a source file named hello.c or a target named hello.
When you type make by itself, make takes the first target listed in the makefile as the target to be made, but your makefile contains no targets whatsoever, hence No targets. Stop. In short, make has no clue that there is anything nearby with a name like hello*.
With your makefile as is, typing make hello will do what you want, as it tells make what it is that you'd like to build.
If you tell make about hello, you'll also be able to type just make to do what you want:
hello: hello.c
%: %.c
or more idiomatically and flexibly, you would list all your "top-level" targets in an all target:
all: hello
%: %.c
.PHONY: all

Typical c hello world prgram:
hello.c:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
Typical Makefile, short but complete:
Makefile:
all: hello
hello: hello.o
gcc -o "$#" hello.o
hello.o: hello.c
g++ -c hello.c
.PHONY:clean
clean:
rm *.o hello
Example with pattern rules:
all: hello
hello: hello.o
gcc -o "$#" hello.o
%.o: %.c
gcc -c $<
.PHONY:clean
clean:
rm *.o hello
Example with delimiter(\n as enter, \t as tab):
all: hello\n
hello: hello.o\n
\tgcc -o "$#" hello.o
%.o: %.c\n
\tgcc -c $<
.PHONY:clean
clean:\n
\trm *.o hello

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.

How to use GNU make's implicit rules to make `foo` from `foo.s`?

Here's my makefile:
AS=nasm
ASFLAGS=-f elf64
%: %.o
${LD} -o $# $<
Suppose I have a source file foo.s, I can run make foo.o then make foo to make the executable foo.
$ make foo.o
nasm -f elf64 -o foo.o foo.s
$ make foo
ld -o foo foo.o
But if I run make foo directly, cc is used instead.
$ rm foo foo.o
$ make foo
cc -f elf64 foo.s -o foo
cc: error: elf64: No such file or directory
cc: error: unrecognized command line option ‘-f’
make: *** [<builtin>: foo] Error 1
This is explained in the documentation, at the end of the section "Linking a single object file".
How should I write my makefile so that I can run make foo to make foo.o from foo.s and then foo from foo.o automatically?
You can clear the default implicit rule with:
%: %.s
See Canceling Implicit Rules in the make manual.
You could replace:
%: %.o
${LD} -o $# $<
by:
ASEXES := $(patsubst %.s,%,$(wildcard *.s))
$(ASEXES): %: %.o
${LD} -o $# $<
This is a a Static Pattern Rule. For each word in $(ASEXES), it instantiates an explicit rule, not a pattern rule. So, it is treated exactly as if you had explicitly written:
foo: foo.o
${LD} -o $# $<
for each foo in $(ASEXES), while pattern rules like:
%: %.o
${LD} -o $# $<
are treated differently (see the section entitled Static Pattern Rules versus Implicit Rules of the GNU make manual).

Define a choice of prerequisites in a pattern rule

For example, lets say I have a compiler that can build foo files from either bar or baz sources.
The rules for this might look like:
%.foo: %.bar
# commands to
# invoke compiler
%.foo: %.baz
# commands to
# invoke compiler
However, this could start getting a bit long and redundant as the number of input types and recipe commands increase. Is there any syntax available to compress this into a single rule?
%.foo: $(oneof %.bar %.baz)
# commands to
# invoke compiler
What you propose at the beginning is right: Makefiles should be clear and concise regarding building rules.
In the other hand you may take a look at Canned Recipes to try to avoid repeating the same recipes once and again:
define MAKE_FOO =
#You may use automatic variables such as $^ or $#.
mv $< $# #In this example just a file renaming.
endef
%.foo: %.bar
$(MAKE_FOO)
%.foo: %.baz
$(MAKE_FOO)
The canned recipe MAKE_FOO will expand to whatever recipes you write inside the define statement as if they were copied manually.
Here's an illustration for the concrete problem of making an .o file
from either a .c file or a .cpp file with a combined pattern rule.
An executable is also built to aid the illustration.
Makefile
.PHONY: all clean
all: test
%.o: %.c %.cpp
gcc -c $?
test: main.o hw.o
g++ -o $# $^
clean:
rm -f test *.o
where we have:
hw.c
#include <stdio.h>
void hw(void)
{
puts("Hello from C");
}
hw.cpp
#include <iostream>
extern "C" void hw()
{
std::cout << "Hello from C++" << std::endl;
}
and:
main.cpp
extern "C" void hw(void);
int main(void)
{
hw();
return 0;
}
Make from clean and run:
$ make clean && make && ./test
rm -f test *.o
g++ -c -o main.o main.cpp
gcc -c hw.c hw.cpp
g++ -o test main.o hw.o
Hello from C++
Both hw.c and hw.cpp were compiled per the pattern rule.
Each one of them was compiled to the same object file, hw.o, with the second, C++
compilation overwriting the C compilation. So the C++ object file was linked,
simply because it was the last to be built. Be clear about what you expect to
happen when the combined rule is triggered by multiple prerequisites.
Now let's update hw.c and repeat:
$ touch hw.c
$ make && ./test
gcc -c hw.c
g++ -o test main.o hw.o
Hello from C
This time, hw.o was compiled only from hw.c, and linked.
Update hw.cpp and repeat:
$ touch hw.cpp
make && ./test
gcc -c hw.cpp
g++ -o test main.o hw.o
Hello from C++
Once again, the hw.o from C++ was linked.
The key element of the combined pattern rule is $?, which
means all the prerequisites that are newer than the target

Makefile pattern matching failure

BINS = $(patsubst %.c, %, $(SRCS))
all: $(BINS)
%: %.o
$(info ---------- linking $< ---------)
$(CC) $< -o $# -L$(LIBPATH) -llibrary
Will name in $(BINS) match %? I need the rule %: %.o to be invoked which in turn calls other rule to create .os. But this match is not happening due to which implicit rules are getting triggered.
My goal is to create binaries with same name as their .c files with out the extension. .os should be created in the process
This might sound drastic, but you should remove all rules, then it will work. Yes, I'm serious. make has built-in rules to create foo from foo.c by compiling, since it is so common. Watch it happen:
$ cat hello.c
#include <stdio.h>
int main (void)
{
printf ("hello, world\n");
return 0;
}
$ cat Makefile
cat: Makefile: No such file or directory
$ make CFLAGS=-lm hello
cc -lm hello.c -o hello
$ ./hello
hello, world
$
More realistically, if you want to compile a set of executables each from their *.c counterpart, all you need in your Makefile is
all: prog1 prog2 progN
Sweet, ain't it?
This works for me:
all : ${BINS}
%.o: %.c
$(CC) -c -o $# $<
$(BINS) : %: %.o
#echo ---------- linking $< ---------
$(CC) -o $# $< -L$(LIBPATH) -llibrary
It would be simpler to compile and link in one go:
$(BINS) : %: %.c
#echo ---------- compiling and linking $< ---------
$(CC) -o $# $<

Resources