I am compiling a simple program that uses the SDL2 library.
MWE (an oversimplified version of Lazy Foo's tutorial):
// main.cpp
#include <SDL.h>
int main(int argc, char* args[])
{
SDL_Window* window = NULL;
SDL_Surface* screenSurface = NULL;
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("The exciting white window", DL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN);
screenSurface = SDL_GetWindowSurface(window);
SDL_FillRect(screenSurface, NULL, SDL_MapRGB( screenSurface->format, 0xFF, 0xFF, 0xFF));
SDL_UpdateWindowSurface(window);
SDL_Delay(2000);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
I would like to adapt the Makefile from this tutorial to handle it.
This is what I tried:
program_NAME := main
program_CXX_SRCS := $(wildcard *.cpp)
program_CXX_OBJS := ${program_CXX_SRCS:.cpp=.o}
program_OBJS := $(program_CXX_OBJS)
program_INCLUDE_DIRS := /usr/include/SDL2
program_LIBRARY_DIRS :=
program_LIBRARIES := SDL2
CPPFLAGS += $(foreach includedir,$(program_INCLUDE_DIRS),-I$(includedir))
LDFLAGS += $(foreach librarydir,$(program_LIBRARY_DIRS),-L$(librarydir))
LDFLAGS += $(foreach library,$(program_LIBRARIES),-l$(library))
.PHONY: all clean distclean
all: $(program_NAME)
$(program_NAME): $(program_OBJS)
$(LINK.cc) $(program_OBJS) -o $(program_NAME)
clean:
@- $(RM) $(program_NAME)
@- $(RM) $(program_OBJS)
distclean: clean
However, I get several linker errors:
$ make
g++ -I/usr/include/SDL2 -c -o main.o main.cpp
g++ -I/usr/include/SDL2 -lSDL2 main.o -o myprogram
main.o: In function `main':
main.cpp:(.text+0x25): undefined reference to `SDL_Init'
main.cpp:(.text+0x4a): undefined reference to `SDL_CreateWindow'
main.cpp:(.text+0x5a): undefined reference to `SDL_GetWindowSurface'
main.cpp:(.text+0x7d): undefined reference to `SDL_MapRGB'
main.cpp:(.text+0x90): undefined reference to `SDL_FillRect'
main.cpp:(.text+0x9c): undefined reference to `SDL_UpdateWindowSurface'
main.cpp:(.text+0xa6): undefined reference to `SDL_Delay'
main.cpp:(.text+0xb2): undefined reference to `SDL_DestroyWindow'
main.cpp:(.text+0xb7): undefined reference to `SDL_Quit'
collect2: error: ld returned 1 exit status
Makefile:18: recipe for target 'myprogram' failed
make: *** [myprogram] Error 1
I understood from this answer that the order of the arguments now matters. Invoking
$ g++ -I/usr/include/SDL2 -c -o main.o main.cpp
$ g++ main.o -lSDL2 -o main
directly from the command line works.
So finally my question: what is the correct way of adapting the Makefile? (By "correct" I mean avoiding the type of pitfalls the Makefile tutorial above mentions.)
There is an error in the tutorial makefile you have found. To fix the makefile, replace the line:
LDFLAGS += $(foreach library,$(program_LIBRARIES),-l$(library))
with:
LDLIBS += $(foreach library,$(program_LIBRARIES),-l$(library))
and replace the line:
$(LINK.cc) $(program_OBJS) -o $(program_NAME)
with:
$(LINK.cc) $(program_OBJS) -o $(program_NAME) $(LDLIBS)
or in more normal style:
$(LINK.cc) $^ -o $@ $(LDLIBS)
(See 10.5.3 Automatic Variables in the manual)
The error in the tutorial makefile is that is that it doesn't correctly
employ (or explain) the canonical make-variables LDFLAGS and LDLIBS. It
bundles all of the linker's library options (-l<name>) into the value of LDFLAGS.
They are not supposed to go there. They should go in the value of LDLIBS.
The predefined make variable LINK.cc expands to:
LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
which does not include $(LDLIBS), precisely because the GNU linker, as
used in most GCC distributions (including yours), requires object files to appear
earlier in the linkage sequence than the libraries they depend on, and will
ignore a library at a position in the sequence where it does not resolve any symbol
references that the linker already needs to resolve.
If LDLIBS is correctly employed for the -l
options, and correctly employed in a linkage recipe - as in the corrected linkage recipe above
- then all libraries will safely appear after all object files.
By bundling -l options into $(LDFLAGS), the tutorial gets all the libraries
before all the object files; so in your linkage, -lSDL2 is ignored and your
linkage fails as observed.
Here is a crib to the canonical GNU make-variables that are typically operative in C or C++ compilation and linkage:
CC Command that invokes your C compiler (default cc)
CXX Command that invokes your C++ compiler (default g++)
CPPFLAGS Your options for the C/C++ preprocessor
CFLAGS Your options for the C compiler
CXXFLAGS Your options for the C++ compiler
LDFLAGS Your options for linkage, excluding libraries and library (-l) options
LDLIBS Your linkage libraries or library (-l) options
The default value is empty unless otherwise specified.
See 10.2 Catalogue of Built-In Rules
to learn how these variables work in make's builtin rules. Or save the output of
make --print-data-base to a file and study it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With