Caro Caro - 6 months ago 23
Linux Question

C : Unix SDL2 library : undefined reference, issue in the Makefile?

I'm looking to make a Makefile that can compile a C program on Unix (Ubuntu). That Makefile should include the SDL library (2.0), as well as SDL_image.

So here's my current Makefile :

CC = gcc
OBJECTS = $(patsubst %.c,%.o,$(wildcard *.c))
EXEC = main

LDFLAGS = `sdl2-config --libs` -L/usr/lib -lSDL2_image
CCFLAGS = `sdl2-config --cflags` -I/usr/include/SDL_image.h

$(EXEC): $(OBJECTS)
$(CC) $(LDFLAGS) $< -o $@

%.o: %.c
$(CC) -c $(CCFLAGS) $< -o $@

.PHONY: clean

clean:
rm -f $(OBJECTS) $(EXEC)


And here's my current code (minimal code just to test the Makefile):

#include <stdio.h>
#include <SDL.h>
#include <SDL2/SDL_image.h>

int main(int argc, char **argv) {

SDL_Window* window = NULL;
SDL_Surface* screenSurface = NULL;

if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("Initialization error: %s\n",SDL_GetError());
return 1;
}

window = SDL_CreateWindow("Test",SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,640, 480,SDL_WINDOW_SHOWN);

SDL_Quit();
return 0;
}


So when I write the command : make

I get errors on all of the SDL functions.
Here's an example : undefined reference to `SDL_Init'

I have tried many things (such as different paths for the Include and Link in the Makefile), but nothing seems to work.

So my question is : How can I solve theses undefined references to SDL2 functions ?

Answer

You are specifying linker flags in the wrong order.

This is wrong:

LDFLAGS = `sdl2-config --libs` -L/usr/lib -lSDL2_image
$(EXEC): $(OBJECTS)
    $(CC) $(LDFLAGS) $< -o $@

This is correct:

LIBS = `sdl2-config --libs` -L/usr/lib -lSDL2_image
$(EXEC): $(OBJECTS)
    $(CC) $(LDFLAGS) $^ $(LIBS) -o $@

With GNU Binutils, the order of libraries is important. Libraries must come after the objects which have undefined references to symbols in those libraries. It sucks, it's stupid, but it's the way GNU Binutils works and you're stuck with it.

Other fixes

  • You want $^ instead of $<, because $< is only the first dependency.

  • Probably best to use pkg-config which can find SDL2_image for you:

    LIBS = `pkg-config --libs sdl2 SDL2_image`
    
  • You probably want := not = when you invoke shell programs, this will expand the variable once, instead of every time it is used:

    LIBS := $(shell pkg-config --libs sdl2 SDL2_image)
    CFLAGS := $(shell pkg-config --cflags sdl2 SDL2_image)
    

Full example

Here is a cleaned up example, close to how I would write it:

# use := not =
# convention: objects, exec are lower-case because they're private
objects := $(patsubst %.c,%.o,$(wildcard *.c))
CFLAGS := $(shell pkg-config --cflags sdl2 SDL2_image)
LIBS := $(shell pkg-config --libs sdl2 SDL2_image)

# Don't define CC, because the default (cc) is fine
# It's probably linked to gcc on your system anyway

# = or := doesn't matter here
exec = main

# must use = here
depflags = -MF $(patsubst %.o,%.d,$@) -MMD -MP
-include $(wildcard *.d)

$(exec): $(objects)
    $(CC) $(LDFLAGS) $^ $(LIBS) -o $@

%.o: %.c
    $(CC) $(depflags) -c $(CCFLAGS) $< -o $@

.PHONY: clean
clean:
    rm -f *.o *.d $(exec)