C:Unix SDL2 库:未定义的引用,Makefile 中的问题?

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

我正在寻找可以在 Unix 上编译 C 程序的 Makefile (Ubuntu)。该 Makefile 应包括 SDL 库 (2.0) 以及 SDL_image.

所以这是我当前的 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)

这是我当前的代码(用于测试 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;
}

所以当我写命令时:make

我在所有 SDL 函数上都遇到了错误。 下面是一个示例:未定义的对 `SDL_Init'

的引用

我尝试了很多方法(例如 Makefile 中的 Include 和 Link 的不同路径),但似乎没有任何效果。

所以我的问题是:如何解决这些对 SDL2 函数的未定义引用?

您颠倒了一些 compiler/linker 选项:

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

应该是:

# linker options
LDFLAGS = `sdl2-config --libs` -L/usr/lib -lSDL2_image
# compiler options
CCFLAGS = `sdl2-config --cflags` -I/usr/include

详情:

-L Is to indicate to the linker where to find .so files
-l Is to link to a given library
-I Is to indicate to the compiler where to find .h files

您以错误的顺序指定了链接器标志。

这是错误的:

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

这是正确的:

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

对于 GNU Binutils,库的顺序很重要。库必须位于对这些库中的符号具有未定义引用的对象之后。它很糟糕,很愚蠢,但这是 GNU Binutils 的工作方式,你会坚持使用它。

其他修复

  • 您需要 $^ 而不是 $<,因为 $< 只是 first 依赖项。

  • 可能最好使用 pkg-config 可以为您找到 SDL2_image:

    LIBS = `pkg-config --libs sdl2 SDL2_image`
    
  • 当您调用 shell 程序时,您可能希望 := 而不是 =,这将扩展变量一次,而不是每次使用它时:

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

完整示例

这是一个经过整理的示例,接近于我的编写方式:

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

可以看出Catalogue of Built-In Rules:

Linking a single object file

n is made automatically from n.o by running the linker (usually called ld) via the C compiler. The precise recipe used is:

$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)

Variables Used by Implicit Rules:

LDFLAGS

Extra flags to give to compilers when they are supposed to invoke the linker, ld, such as -L. Libraries (-lfoo) should be added to the LDLIBS variable instead.

所以在这种情况下 -lSDL2_image 应该设置或添加到 LDLIBS,而不是 LDFLAGS。