Why is this Makefile wrong? Linker fails only if preceded by compiler error - c

This is the example program to make it easier to explain. Have this 4 files, main.c, sum.c and header.h. and finally the Makefile.
main.c
#include <stdio.h>
#include "header.h"
int main()
{
int a = 1;
int b = 1;
int c = sum(a, b);
printf("Sum: %i\n",c);
return (0);
}
sum.c
#include "header.h"
int sum(int a, int b)
{
return (a + b);
}
header.h
int sum(int a, int b);
Makefile
TEST_SRCS = *.c
TEST_OBJS = $(TEST_SRCS:%.c=%.o)
%.o : %.c *.h
gcc -c $^
assemble_test: $(TEST_OBJS);
test: assemble_test
gcc -o test *.o
./test
clean:
#rm -rf *.o *.gch test
#echo "Cleaned"
Steps to reproduce:
Call test rule: make test > all ok.
Introduce any error on purpose, example two commas:
printf("Sum: %i\n",,c);
Call test rule: make test > Getting normal syntax error.
Remove the error. Save file.
Call test rule: make test > Getting linker error no matter what.
gcc -o test *.o
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [test] Error 1
Use clean rule to start again every time.
Have this behaviuor for a while and i cant figure the problem. To be honest im new to Makefiles so i must be doing something very wrong.
About the test rule calling assemble_test. Its because part of a larger Makefile and i need diferent rules for diferent purposes to compile the objects, so dont mind.
Can you please help me understand what is going on?
Thanks.

It's wrong to use wildcards for the object files. Wildcards match files that already exist on the disk when make runs. That doesn't help because obviously they won't match files that do not yet exist, and the entire point of your makefile is to create files that don't exist.
So this:
TEST_SRCS = *.c
TEST_OBJS = $(TEST_SRCS:%.c=%.o)
leaves the value of TEST_SRCS the string *.c and the value of TEST_OBJS the string *.o (perhaps you were thinking that wildcards are expanded immediately, somehow? No, that's not the case).
Then this line:
assemble_test: $(TEST_OBJS);
causes the target assemble_test to list *.o as a prerequisite: this will be expanded to all the object files that already exist in the directory. But of course it won't expand to object files that you want make to create, but don't exist yet.
There are lots of other problems here: for example you don't want to compile $^ because that expands to all the prerequisites but you only want to compile source files, not header files. And the addition of assemble_test seems odd and makes things harder.
You probably want this, assuming you're using GNU make:
TEST_SRCS := $(wildcard *.c )
TEST_OBJS = $(TEST_SRCS:%.c=%.o)
%.o : %.c *.h
gcc -c $<
test: $(TEST_OBJS)
gcc -o $# $^
./$#
By using the wildcard function you expand the value of the sources glob first before you replace the results with .o.

Related

gcc: why does linking fail when rebuilding after changing .c file but not when initially building?

I have the following files:
main.c
#include "other.h"
int main(int argc, char *argv[]) {
other();
return 0;
}
other.c
#include "other.h"
void other(void) {
1+1;
}
other.h
#ifndef OTHER_H
#define OTHER_H
void other(void);
#endif
and I am using this makefile:
OBJ = other.o main.o
main: $(OBJ)
gcc $(OBJ) -o $#
main.o: main.c
gcc -c main.c
other.o: other.c
gcc -c other.c
clean:
rm -f $(OBJ) main
When I run make clean && make, everything compiles and links successfully.
Then, I make a whitespace change to other.c.
Now, when I run make, I get the following error:
gcc other.o main.o -o main
main.o: In function `main':
main.c:(.text+0x5): undefined reference to `other'
collect2: error: ld returned 1 exit status
Makefile:6: recipe for target 'main' failed
make: *** [main] Error 1
Why does linking fail after updating other.c?
Note that linking works if I make a whitespace change to main.c, or if I again run make clean.
UPDATE: In case it's useful, I compared the other.o file in the working case with the other.o file in the broken case; they have a slight difference. I have no idea what would cause this difference, nor how to interpret it. See image: difference between other.o, working case and broken case
OK, I think I figured this out. It seems to be happening because of the linter I have in my text editor (Atom version 1.33, linter version 2.2.0, linter-gcc version 0.7.1).
When I update the file other.c in Atom and save, the file other.o is also updated via the linter. If I compare the symbols in other.o, before it includes other_function, but after it includes _Z14other_functionv. The updated timestamp on other.o also explains why make is not running gcc -c other.c.
When I make the whitespace change using a different text editor, make works correctly.

Allegro5 makefile error

note: this is the final exercise in the Head First C book.
I have the following problem. I am trying to make a game using the allegro5.2 libraries. I want to use multiple .c files in order to organize everything neatly. However, I have problems compiling my programs using a makefile. I am trying to compile this easy program:
#include <stdio.h>
#include <allegro5/allegro.h>
const int disp_h = 640;
const int disp_w = 480;
int main(int argc, char **argv) {
ALLEGRO_DISPLAY *display;
if(!al_init()) {
fprintf(stderr, "failed to initialize allegro!\n");
return -1;
}
display = al_create_display(disp_h,disp_w);
if(!display) {
fprintf(stderr, "failed to create display!\n");
return -1;
}
al_rest(0.4);
al_destroy_display(display);
printf("bye bye!!!\n");
return 0;
}
The makefile is:
Blasteroids.o: allegro.h Blasteroids.c
gcc -Wall -c Blasteroids.c
Blasteroids: Blasteroids.o allegro.h
gcc -Wall -I/usr/include/allegro5 -L/usr/lib -lallegro -lallegro_main Blasteroids.o -o Blasteroids
Now, when I use the terminal this compiles fine, but now I seem to have a problem. The error given by the terminal (using the command make Blasteroids) is:
cc Blasteroids.o -o Blasteroids
Undefined symbols for architecture x86_64:
"_al_create_display", referenced from:
__al_mangled_main in Blasteroids.o
"_al_destroy_display", referenced from:
__al_mangled_main in Blasteroids.o
"_al_install_system", referenced from:
__al_mangled_main in Blasteroids.o
"_al_rest", referenced from:
__al_mangled_main in Blasteroids.o
"_main", referenced from:
implicit entry/start for main executable
(maybe you meant: __al_mangled_main)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [Blasteroids] Error 1
I don't know what I am doing wrong and I am very new to these things. I have searched for examples in makefiles, but they give me code like I am using now.
I now I can just use a single line for the above program, but the idea is that I want to make my own .c files, make them into .o files and then link them together. Hence the makefile.
The make program looks for files named makefile or Makefile, with no extension. If you name your makefile something else, such as makefile.txt, then make can't find it and it will just use its own built-in rules which don't know anything about extra flags ro libraries that may be needed.
So either rename your makefile to makefile or Makefile, or else specify the name of your makefile explicitly on the command line when you run make, such as make -f makefile.txt Blasteroids.
Secondarily, if you don't specify a target on the command line then make will always build the first target. So, if you re-order your targets so that the one you usually want to build (in this case Blasteroids) is first, then you can just run make with no arguments and it will build that target.
Unlike a programming language, the order of target definition doesn't matter to make: e.g., you don't have to define rules for all the object files first before the link line. Make reads the entire file and constructs an internal graph of prerequisite relationships, and nodes and edges in this graph can be added in any order.
something like the following makefile contents should do the job
Notes:
header files are only needed during the compile step
library files are only needed during the link step
best to have the first 'target' be 'all' so the makefile can be successfully executed without have to include a 'Blasteroids' parameter
having a 'target' 'clean' makes it easy to clear away the items that will be rebuilt
any 'target' that does not produce a file of the same name should be listed in a '.PSEUDO' operator
the linker processes the linker command line items in the order listed on the command line. Therefore, list the objects, then the directories, then the library 'short' names
list the header files at the end of the compile command line
use macros (like CC and RM) that contain the full path to the executables so the correct executable is used, other wise the first item found in the $PATH that matches the name will be used.
$^ is a built in macro that expands to the recipe dependencies
$< is a built in macro that expands to the first dependency
$# is a built in macro that expands to the recipe 'target'
the $(SRC:.c=?) statements do character replacement on the extension of the file names contained in the $(SRC) macro
the %.o:%.c recipe says for each source file to be compiled into a object file, use this recipe
the '-ggdb' is so the maximum debug information will be available to the 'gdb' debugger.
this pattern of 'makefile' can be extensively used for other projects with only minor variations. For more flexibility could also insert a recipe for automatically generating the header file dependencies, so would not need to list them individually in the compile recipe, but for now, the following makefile contents will do what you are currently trying to perform
and now the makefile
CC := /bin/gcc
RM := /bin/rm
CFLAGS := -Wall -Wextra -pedantic -std=gnu99 -ggdb -c
LFLAGS := -L/usr/lib -lallegro -lallegro_main
SRC := Blasteroids.c
#OBJ := $(SRC:.c=.0)
OBJ := $(SRC:.c=.o)
NAME := $(SRC:.c=)
.PSEUDO: all clean
all: $(NAME)
%.o:%.c
#$(CC) $(CFLAGS) $^ -o $# -I/usr/include/allegro5
$(CC) $(CFLAGS) $< -o $# -I/usr/include/allegro5
$(NAME): $(OBJ)
$(CC) -ggdb $^ -o $# $(LFLAGS)
.clean:
$(RM) -f $(NAME) $(OBJ)

make rules with existing target, existing prerequisites but no recipe

I was looking at the net-snmp code and I found something odd in the Makefile of the snmplib itself. The last couple of thousands of lines are nothing but rules in this form:
./dir_utils.lo: ../include/net-snmp/output_api.h
for EVERY library object and header file. Even those that are not compiled, depending on which flags are selected in the configure script.
I looked at the "make" manual but I didn't find this exact case. It may be (as stated in 5.9) that they do this to exclude the possibility that an implicit recipe is called on the target, but other than that I have no idea.
Another reason might be to "break" the compilation if the library is tampered with (deleting whatever header in the project causes in fact the makefile to crash because it can't execute the rule).
This is an educated guess but I would like to know the theory behind this. I mean the makefile already builds whatever it has to build, why include all these rules in explicit form?
Thanks
As I expect you know, the Makefile is generated by the ./configure script.
All the lines that interest you are auto-generated dependencies. Actually,
they are just the appended contents of the file Makefile.depend in the same
directory, which is part of the distribution and was generated with the aid
of gcc ahead of time.
So e.g.
./dir_utils.lo: ../include/net-snmp/output_api.h
just informs make of the vitally important fact that ./dir_utils.lo
depends on ../include/net-snmp/output_api.h. Then if ./dir_utils.lo
is older than ../include/net-snmp/output_api.h, make will re-make
./dir_utils.lo provided it has some recipe to do that, which it has.
Here is a project:
main.c
#include "hw.h"
#include <stdio.h>
int main(void)
{
puts(HW);
return 0;
}
hw.h
#ifndef HW_H
#define HW_H
#define HW "Hello World"
#endif
Makefile
CC := gcc
.PHONY: all clean
all: hw
hw: main.o
$(CC) -o $# $<
clean:
rm -f hw main.o
Build and run it:
$ make && ./hw
gcc -c -o main.o main.c
gcc -o hw main.o
Hello World
But there's a bug in the makefile. It doesn't know that main.o
depends on hw.h:
$ touch hw.h
$ make
make: Nothing to be done for 'all'.
Append that dependency to the makefile:
main.o: hw.h
and retry:
$ make
gcc -c -o main.o main.c
gcc -o hw main.o
Bug fixed.

Linking error: Undefined reference to functions that're defined in a separate file?

I'm probably forgetting something obvious that'll solve this. While there's other questions on SO with the same issue, none of the solutions have been applicable to my situation.
I have a main file, sim.c, a header file net.h, a header file friends.h, and a file with the functions, net.c. I have a makefile, which I created with gmakemake > Makefile, and its contents are very basic.
Header.mak (makefile template):
CFLAGS = -Wall -Wextra -pedantic -std=c99 -ggdb
LFLAGS = -ggdb
LDFLAGS =
Makefile relevant contents:
CPP_FILES =
C_FILES = net.c sim.c
PS_FILES =
S_FILES =
H_FILES = net.h friends.h
SOURCEFILES = $(H_FILES) $(CPP_FILES) $(C_FILES) $(S_FILES)
.PRECIOUS: $(SOURCEFILES)
OBJFILES =
#
# Main targets
#
all: net sim
net: net.o $(OBJFILES)
$(CC) $(CFLAGS) -o net net.o $(OBJFILES) $(CLIBFLAGS)
sim: sim.o $(OBJFILES)
$(CC) $(CFLAGS) -o sim sim.o $(OBJFILES) $(CLIBFLAGS)
#
# Dependencies
#
net.o: net.h
sim.o: net.h
My sim.c file contains:
#include "net.h"
#include "friends.h"
My header file contains the functions in net.c and defines them all as stubs. I copied and pasted them to create the function headers, so there shouldn't be any typos.
My net.c file contains:
#include "net.h"
Yet any time a function in sim.c tries to call a function in net.c, it errors on that line with:
"undefined reference to `function_name`".
How can I make sim.c able to access the functions in net.c?
The message undefined reference to 'function_name' implies that of all the object files you're giving to the linker, none of them has a definition for function_name. That means that either
You're not linking with net.o
net.c (as compiled) does not contain a definition for function_name -- by 'as compiled' I mean with all of the various preprocessor options you use on it.
Since you show neither your link command line nor the contents of net.c, we can't tell which is the problem.
edit
with your edit, we can see clearly that you have the first problem -- when you try to link sim, you do not include net.o on the link command line. Most commonly, you would link sim with a makefile entry like:
sim: sim.o net.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^ $(LDLIBS)
or more simply just:
sim: sim.o net.o
relying on the default make actions for linking object files
For the two functions "see" each other make sure that:
Both c/cpp files are included in the makefile
Both definition(c/cpp) and declaration (h) files contains the same definition of the function: name/params/return value
The function being called must not be static.
Make sure you don't declare (or include) the same type with different structure in the source files.
That should do, unless you are using a very old complier with even more evil things that can go wrong ;)

Automatic actions by makefile

I have written simple code *make_test* to test the functionality of makefile. The folder structure is as follows:
include folder contains maths.h
float add(float a, float b)
{
return a+b;
}
the main file contains the following code:
# include <stdio.h>
# include <include/maths.h>
int main(int argc, char** argv)
{
float a=1;
float b=4;
printf("%f + %f = %f\n", a, b, add(a, b));
return 0;
}
The makefile which contains the following:
IDIR = -I.
DEPS = ./include/maths.h
CC= gcc -Wall -g $(IDIR)
program: make_test.o
%.o: test/%.c $(DEPS)
#echo "Rule 1 was called"
$(CC) -o $# $<
%.o: test/%.c $(DEPS)
#echo "Rule 2 was called"
$(CC) -o $# $<
%.o: test/%.c $(DEPS)
#echo "Rule 3 was called"
$(CC) -o $# $<
Note that there are test/%.c statements that have been deliberately inserted for testing.
There is no test folder. If I make a change in main file and I type make on the command line, I get
gcc -Wall -g -I. -c -o make_test.o make_test.c
My question is that I have not added -c flag anywhere then why does it appear at the output? Shouldn't there be a "Nothing to do" output because no dependency either exists (test/%.c) or is changed (include/maths.h)?
Thanks,
Make contains a large number of built-in rules. You can see them by running something like this: make -p -f /dev/null. One of these rules is being used to compile your code, and it contains the -c flag.
Why is a built-in rule being used? Because your rules you defined do not match. They tell make how to build a .o files from a .c file in the test subdirectory. But the make_test.c file is not in the test subdirectory, so those rules can't be used by make. Thus make looks at its built-in rules to find a rule that can build a .o from a .c in this same directory, and finds one, and uses that.
If you move your make_test.c file into the test subdirectory, then your rules will be used.
Note, though, that make is correct here: you must add the -c flag to the compiler invocation if you want .o output. Your rules will not create an object file, they'll create an exectuable named xxx.o which is pretty confusing.

Resources