【问题标题】:Adapting a Makefile to include SDL2调整 Makefile 以包含 SDL2
【发布时间】:2023-12-07 08:03:01
【问题描述】:

我正在编译一个使用 SDL2 库的简单程序。
MWE(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;
}

我想调整来自this tutorial 的 Makefile 来处理它。
这是我尝试过的:

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

但是,我收到了几个链接器错误:

$ 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

我从this answer 了解到,参数的顺序现在很重要。调用

$ g++ -I/usr/include/SDL2 -c -o main.o main.cpp
$ g++ main.o -lSDL2 -o main

直接从命令行工作。

所以最后我的问题:改编 Makefile 的正确方式是什么? (“正确”是指避免上面提到的 Makefile 教程中的陷阱类型。)

【问题讨论】:

    标签: c++ makefile linker sdl-2


    【解决方案1】:

    您发现的教程生成文件中有错误。要修复 makefile,请替换以下行:

    LDFLAGS += $(foreach library,$(program_LIBRARIES),-l$(library))
    

    与:

    LDLIBS += $(foreach library,$(program_LIBRARIES),-l$(library))
    

    并替换该行:

    $(LINK.cc) $(program_OBJS) -o $(program_NAME)
    

    与:

    $(LINK.cc) $(program_OBJS) -o $(program_NAME) $(LDLIBS)
    

    或更普通的风格:

    $(LINK.cc) $^ -o $@ $(LDLIBS)
    

    (见10.5.3 Automatic Variablesthe manual)

    教程makefile中的错误是它不正确 使用(或解释)规范的生成变量LDFLAGSLDLIBS。它 将链接器的所有库选项 (-l&lt;name&gt;) 捆绑到 LDFLAGS 的值中。 他们不应该去那里。他们应该进入LDLIBS的值。

    预定义的 make 变量 LINK.cc 扩展为:

    LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
    

    其中包含$(LDLIBS),正是因为GNU链接器,如 在大多数 GCC 发行版(包括您的发行版)中使用,需要出现目标文件 在链接序列中比它们所依赖的库更早,并且将 忽略序列中不解析任何符号的位置的库 链接器已经需要解析的引用。

    如果LDLIBS 正确用于-l 选项,并在链接配方中正确使用 - 如上面更正的链接配方 - 然后所有库将安全地出现在所有目标文件之后。

    通过将 -l 选项捆绑到 $(LDFLAGS) 中,本教程获取所有库 在所有目标文件之前;所以在你的链接中,-lSDL2 被忽略,你的 正如观察到的那样,链接失败。

    这里是典型的 GNU make-variables 的婴儿床,通常 在 C 或 C++ 编译和链接中操作:

    CC 调用 C 编译器的命令(默认为 cc

    CXX 调用 C++ 编译器的命令(默认为 g++

    CPPFLAGS C/C++ 预处理器的选项

    CFLAGS C 编译器的选项

    CXXFLAGS C++ 编译器的选项

    LDFLAGS 您的链接选项,不包括库和库 (-l) 选项

    LDLIBS您的链接库或库 (-l) 选项

    除非另有说明,否则默认值为空。

    10.2 Catalogue of Built-In Rules 了解这些变量如何在 make 的内置规则中工作。或者保存输出 make --print-data-base 到一个文件并研究它。

    【讨论】: