Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adapting a Makefile to include SDL2

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.)

like image 393
Dominik Mokriš Avatar asked Oct 22 '25 06:10

Dominik Mokriš


1 Answers

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.

like image 56
Mike Kinghan Avatar answered Oct 24 '25 18:10

Mike Kinghan