I have a directory that contains both headers and source for a small collection of hobby OpenGL programs. My goal is create two main header files: "toolbox.h" and "geometry.h". The second #include
s the first. I would also like the sources associated with these two headers to be lumped as two archives for convenience. When I make 01_Planet-Geom
, none of the symbols from "toolbox.h" are found, despite me making its archive part of the make rule. make 00_Dyn-6D
works just fine.
What do I need to change in order to make 01_Planet-Geom
?
01_Planet-Geom: 01_Planet-Geom.o toolbox.a geometry.a
gcc $(CFLG) -o $@.out $^ $(LIBS)
make 01_Planet-Geom
from the project directory.I get the following output:
gcc -c -O3 -Wall -std=gnu17 01_Planet-Geom.c
gcc -c -O3 -Wall -std=gnu17 TriNet.c
ar -rcs geometry.a toolbox.a TriNet.o
gcc -O3 -Wall -std=gnu17 -o 01_Planet-Geom.out 01_Planet-Geom.o toolbox.a geometry.a -lglut -lGLU -lGL -lm
/usr/bin/ld: geometry.a(TriNet.o): in function `N_from_VF':
TriNet.c:(.text+0x151): undefined reference to `get_CCW_tri_norm'
... and many more like it. These are all functions defined in "vector-f_ops.c" ...
/usr/bin/ld: TriNet.c:(.text+0x1abd): undefined reference to `dot_vec4f'
collect2: error: ld returned 1 exit status
make: *** [makefile:50: 01_Planet-Geom] Error 1
I have the following in "makefile":
########## COMPILATION SETTINGS ##################
CFLG=-O3 -Wall -std=gnu17
LIBS=-lglut -lGLU -lGL -lm
CLEAN=rm -f *.out *.o *.a
########## DEPENDENCIES ##########################
### Prog Dependencies ###
00_Dyn-6D.o: 00_Dyn-6D.c toolbox.h
01_Planet-Geom.o: 01_Planet-Geom.c toolbox.h geometry.h
### Float Matrix Math ###
matrix4x4f.o: matrix4x4f.c matrix4x4f.h
### OpenGL Toolbox ###
OGL_utils.o: OGL_utils.c toolbox.h
load_assets.o: load_assets.c toolbox.h
vector-f_ops.o: vector-f_ops.c toolbox.h
### Geometry Construction ###
TriNet.o: TriNet.c toolbox.h geometry.h
########## ARCHIVES & GENERAL RULES ##############
toolbox.a: OGL_utils.o load_assets.o vector-f_ops.o
ar -rcs $@ $^
geometry.a: toolbox.a TriNet.o
ar -rcs $@ $^
# Compile rules
.c.o:
gcc -c $(CFLG) $<
########## PROGRAM & CLEAN RULES #################
##### Program Rules ##############################
00_Dyn-6D: 00_Dyn-6D.o toolbox.a
gcc $(CFLG) -o $@.out $^ $(LIBS)
01_Planet-Geom: 01_Planet-Geom.o toolbox.a geometry.a
gcc $(CFLG) -o $@.out $^ $(LIBS)
##### Clean Rule #################################
# Clean
clean:
$(CLEAN)
I double checked that all the source files #include
the appropriate headers.
"TriNet.c" has compiled successfully in a different context.
I confirmed that make 00_Dyn-6D
still works after adding the rules for 01_Planet-Geom
and geometry.a
.
Your linkage fails for a combination of two reasons.
The first one
is this:
ar -rcs geometry.a toolbox.a TriNet.o
which results from the Makefile rule:
geometry.a: toolbox.a TriNet.o
ar -rcs $@ $^
You might believe that ar -rcs geometry.a toolbox.a TriNet.o
has the effect of extracting
all the object files from the archive toolbox.a
and adding them to
the target archive geometry.a
, together with the object file TriNet.o
.
In fact, it simply archives the files toolbox.a
and TriNet.o
into
geometry.a
, with result that that geometry.a
is an archive containing
another archive, toolbox.a
, and one object file TriNet.o
.
When an archive such as geometry.a
is input to the linker it only considers
object files that are members of the archive, as listed by ar t geometry.a
.
It will extract from the archive just those member object files that
provide definitions of undefined symbols previously in the output file and statically
link them into the output file, doing this iteratively until the archive yields
no more needed definitions.
The linker will not consider object files that are members of
archives that are members of geometry.a
. Therefore what geometry.a
can
contribute to your linkages is just TriNet.o
, which of course is not
enough.
If you want geometry.a
to have as members all the object files in toolbox.a
as well as TriNet.o
then you would need to make geometry.a
from the same object files
that you put into toolbox.a
, plus TriNet.o
.
If ar -rcs geometry.a toolbox.a TriNet.o
worked as it seems you expect it
to, then it would be unnecessary to link both geometry.a
and toolbox.a
into
programs requiring geometry.a
and you would need some other reason for toolbox.a
to exist (which it seems you do).
But evidently you still think it necessary to input both of them in the linkage
of your program 01_Planet-Geom.out
.
gcc -O3 -Wall -std=gnu17 -o 01_Planet-Geom.out 01_Planet-Geom.o toolbox.a geometry.a -lglut -lGLU -lGL -lm
whose linkage fails:
/usr/bin/ld: geometry.a(TriNet.o): in function `N_from_VF':
TriNet.c:(.text+0x151): undefined reference to `get_CCW_tri_norm'
Which brings us to:
The second one
geometry.a
contains only the object file TriNet.o
as far as the linker can see. But TriNet.o
contains an undefined reference to get_CCW_tri_norm
, which (I bet) is defined in a member of toolbox.a
.
You have input toolbox.a
to the linkage but you have input it before the geometry.a
. So when
the linker inspected toolbox.a
it was aware of no undefined reference to get_CCW_tri_norm
and
therefore did not seek, extract, or link any object file from toolbox.a
that provides a definition
of get_CCW_tri_norm
. No file file that is subsequently input provides that definition,
so the symbol remains undefined at end of linkage.
Files that introduce undefined symbols to the linkage must be given to the linker before libraries that provide their definitions. The linker will inspect a library only to find definitions of undefined symbols already in hand, and does not look backwards.