【问题标题】:Allegro5 makefile errorAllegro5 生成文件错误
【发布时间】:2017-01-25 17:49:47
【问题描述】:

注意:这是 Head First C 书中的最后一个练习。

我有以下问题。我正在尝试使用 allegro5.2 库制作游戏。我想使用多个 .c 文件来整齐地组织所有内容。但是,我在使用 makefile 编译程序时遇到问题。我正在尝试编译这个简单的程序:

#include <stdio.h>
#include <allegro5/allegro.h>

const int disp_h = 640;
const int disp_w = 480;

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

ALLEGRO_DISPLAY *display;

if(!al_init()) {
    fprintf(stderr, "failed to initialize allegro!\n");
    return -1;
}

display = al_create_display(disp_h,disp_w);
if(!display) {
    fprintf(stderr, "failed to create display!\n");
    return -1;
}

al_rest(0.4);
al_destroy_display(display);

printf("bye bye!!!\n");

return 0;
}

makefile 是:

Blasteroids.o: allegro.h Blasteroids.c
    gcc -Wall -c Blasteroids.c

Blasteroids: Blasteroids.o allegro.h
    gcc -Wall -I/usr/include/allegro5 -L/usr/lib -lallegro -lallegro_main Blasteroids.o -o Blasteroids

现在,当我使用终端时,它编译得很好,但现在我似乎遇到了问题。终端给出的错误(使用命令make Blasteroids)是:

cc   Blasteroids.o   -o Blasteroids
Undefined symbols for architecture x86_64:
  "_al_create_display", referenced from:
      __al_mangled_main in Blasteroids.o
  "_al_destroy_display", referenced from:
      __al_mangled_main in Blasteroids.o
  "_al_install_system", referenced from:
      __al_mangled_main in Blasteroids.o
  "_al_rest", referenced from:
      __al_mangled_main in Blasteroids.o
  "_main", referenced from:
     implicit entry/start for main executable
     (maybe you meant: __al_mangled_main)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [Blasteroids] Error 1

我不知道我做错了什么,而且我对这些事情很陌生。我已经在 makefile 中搜索了示例,但它们给了我现在正在使用的代码。 我现在可以只为上述程序使用一行,但我的想法是我想制作自己的 .c 文件,将它们制作成 .o 文件,然后将它们链接在一起。因此生成文件。

【问题讨论】:

  • 我真的怀疑 exact 相同的命令是否可以从终端运行。您应该将库标志 -lallegro -lallegro_main 移动到链接行的末尾,在 .o 文件之后。
  • 确实有效。每当我运行程序时,我都会看到一个打开的屏幕
  • 我的意思是,链接命令不是输出程序。无论如何,您的库必须位于链接行中的目标文件之后。
  • 好吧,另外没有使用makefile。您可以看出这一点,因为链接命令不是您在 makefile 中编写的命令。你给makefile起什么名字?它必须是Makefilemakefile,否则如果选择非标准名称,则必须使用make -f mymakefile Blasteroids
  • 像这样:gcc -Wall -I/usr/include/allegro5 -L/usr/lib Blasteroids.o -lallegro -lallegro_main -o Blasteroids?还是在 -o 之后?无论如何,我的文件称为 makefile(但是我使用的是 .txt 扩展名)。这可能是问题吗?对不起,如果这看起来很简单,但我真的很想理解这一点。

标签: c makefile allegro5


【解决方案1】:

make 程序查找名为 makefileMakefile 的文件,没有扩展名。如果您将 makefile 命名为其他名称,例如 makefile.txt,那么 make 将找不到它,它只会使用自己的内置规则,这些规则对可能需要的额外标志 ro 库一无所知。

所以要么将你的 makefile 重命名为 makefileMakefile,要么在运行 make 时在命令行上明确指定你的 makefile 的名称,例如 make -f makefile.txt Blasteroids

其次,如果您没有在命令行上指定目标,那么 make 将始终构建第一个目标。因此,如果您重新排序目标,以便您通常想要构建的目标(在本例中为 Blasteroids)是第一个,那么您可以不带参数运行 make,它将构建该目标。

与编程语言不同,目标定义的顺序无关紧要:例如,您不必在链接行之前首先为所有目标文件定义规则。 Make读取整个文件并构建一个内部前提关系图,该图中的节点和边可以按任意顺序添加。

【讨论】:

    【解决方案2】:

    类似于下面的 makefile 内容应该可以完成这项工作

    注意事项:

    1. 头文件仅在编译步骤中需要
    2. 只有在链接步骤中才需要库文件
    3. 最好将第一个 'target' 设为 'all',这样 makefile 就可以成功执行,而不必包含 'Blasteroids' 参数
    4. 拥有“目标”“干净”可以轻松清除要重建的项目
    5. 任何不生成同名文件的“目标”都应列在“.PSEUDO”运算符中
    6. 链接器按命令行上列出的顺序处理链接器命令行项目。因此,列出对象,然后是目录,然后是库的“短”名称
    7. 在编译命令行末尾列出头文件
    8. 使用包含可执行文件完整路径的宏(如 CC 和 RM),以便使用正确的可执行文件,否则将使用 $PATH 中与名称匹配的第一项。
    9. $^ 是一个内置宏,可扩展为配方依赖项
    10. $
    11. $@ 是一个内置宏,可扩展为配方“目标”
    12. $(SRC:.c=?) 语句对 $(SRC) 宏中包含的文件名的扩展名进行字符替换
    13. %.o:%.c 配方说对于每个要编译成目标文件的源文件,使用这个配方
    14. “-ggdb”是“gdb”调试器可用的最大调试信息。
    15. 这种“makefile”模式可以广泛用于其他项目,只有很小的变化。为了更灵活,还可以插入一个用于自动生成头文件依赖项的配方,因此不需要在编译配方中单独列出它们,但现在,以下 makefile 内容将执行您当前尝试执行的操作

    现在是makefile

    CC := /bin/gcc
    RM := /bin/rm
    
    CFLAGS := -Wall -Wextra -pedantic -std=gnu99 -ggdb -c
    LFLAGS := -L/usr/lib -lallegro -lallegro_main
    
    SRC    := Blasteroids.c
    #OBJ    := $(SRC:.c=.0)
    OBJ    := $(SRC:.c=.o)
    NAME   := $(SRC:.c=)
    
    .PSEUDO: all clean
    
    all: $(NAME)
    
    %.o:%.c
        #$(CC) $(CFLAGS) $^ -o $@ -I/usr/include/allegro5
        $(CC) $(CFLAGS) $< -o $@ -I/usr/include/allegro5
    
    $(NAME): $(OBJ)
        $(CC) -ggdb  $^ -o $@  $(LFLAGS)
    
    .clean:
        $(RM) -f $(NAME) $(OBJ)
    

    【讨论】:

    • 你绝对不应该在你的编译规则中使用$^,你应该使用$&lt;。此外,您的OBJ 设置不正确(您添加的是.0 而不是.o)。我建议使用标准变量,这意味着LDFLAGS 用于链接器标志,如-LLDLIBS 用于-l 等库。我个人建议不要对CCRM 使用完全限定的路径。
    • @MadScientist,我同意纠正您提到的按键错误。但是,当多个可执行文件具有相同名称时,如果您希望 makefile 使用的可执行文件不是在缓存或 $PATH 列表中找到的第一个可执行文件,则将执行错误的可执行文件。这就是为什么应该始终为任何操作系统功能使用“完全限定”路径,例如 gcc 和 rm 和 mv 以及 chgrp 和 chmod 以及......标准宏:“LDFLAGS”和“LDLIBS”可能不是你想要的你的makefile。所以无论如何你都会替换它们。最好用唯一的宏名称替换。
    • 我将编辑我的答案以反映对 keypunch 错误的更正,注意:makefile 行中的前导 # 表示该行仅是 cmets
    • 您应该在配方行的第 1 列中使用注释字符,而不是在 TAB 之后。 GNU make 不会特别处理出现在配方行中的注释字符,因此该命令将作为运行命令传递给 shell(shell 会将其视为注释,因为它使用相同的字符)。
    • 标准变量的目的是允许用户覆盖变量设置来修改makefile规则的行为方式。 IMO总是更好/更正确地重新使用标准变量(当然,它们的预期用途是这些),而不是发明人们不熟悉且不确定如何使用的新变量。另外,我当然熟悉 PATH 及其作用;最好让用户使用 PATH 来配置他们的系统。也许一个系统有/usr/bin/rm 而不是/bin/rm:现在你的makefile 无缘无故地中断了。但是,当然是 YMMV。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-02
    • 2011-10-14
    • 2023-03-26
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多