Makefile:wildcard和patsubst不会更改文件源名称

gautem 发布于 5 天前 c 最后更新 5 天前 0 浏览

我正在尝试为我的项目编写一个Makefile,所有.c.h文件都位于一个名为src的文件夹中,而Makefile看起来像这样 -

CC              := gcc
CFLAGS          := -g -Wall -ansi -pedantic -std=gnu99
LDFLAGS         := -lm 
INCLUDES        := $(wildcard src/*.h)
IFLAGS          := $(addprefix -I/,$(INCLUDES))
SRC             := $(wildcard src/*.c)
OBJS            := $(patsubst %.c, %.o, $(SRC))
APP             := app
all: $(OBJS)
$(APP): $(OBJS)
    $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
$(OBJS): $(SRC) $(INCLUDES)
    $(CC) $(CFLAGS) $(IFLAGS) -c $< -o $@
clean:
    rm -rf $(OBJS)
    rm -rf *.out
    rm -f $(APP)
在这一点上,我不建立可执行文件,只是试图将它们编译为目标文件,所以当我运行时,我得到了这个输出 -
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/allocate.o
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/auxiliary.o
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/decode.o
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/display.o
您可以看到,在每个gcc调用中,源文件名不会更改,它们都始终是src/allocate.c为什么?但是,对象名称正确展开,如src/allocate.osrc/auxiliary.osrc/decode.oetc。
已邀请:

fomnis

赞同来自:

看来你在这里混淆了一些东西。 它们基本上是你需要在这里使用的两种规则,它们都具有相同的语法:

targets : prerequisites
    recipe
当你写这个:
$(APP): $(OBJS)
    $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
您要说的是要创建$(APP),为此,您需要$(OBJS)存在或创建。 现在当你写这个:
$(OBJS): $(SRC) $(INCLUDES)
    $(CC) $(CFLAGS) $(IFLAGS) -c $< -o $@
你告诉你要创建一个.o文件列表,并且对于你需要所有$(SRC)$(INCLUDES)的每个文件。 由于在配方中您正在使用$<(这是先决条件列表中第一个条目的快捷方式),因此您始终会使用正在编译的相同源文件。
为了做你想做的事,你必须抽象一点并告诉make“这就是我希望你构建任何依赖于相应的.c.o文件”。这是模式规则的工作:
%.o: %.c
    $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
最终,您的Makefile应如下所示:
APP             := app
SRC             := $(wildcard src/*.c)
OBJ             := $(SRC:.c=.o)
CFLAGS          := -W -Wall -g -std=c99 -pedantic
LDLIBS          := -lm
all: $(OBJS)
$(APP): $(OBJ)
    $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
clean:
    $(RM) $(APP) $(OBJ)
请注意您错过的另外几件事:
  • -I预处理程序标志(应放在CPPFLAGS变量中)接受目录,而不是文件。
  • -ansi编译器标志是-std=c89的同义词。您正在使用-std=gnu99,以便最终选择一个
  • 您根本不需要列出头文件。不要打扰。
  • 请勿小心使用rm命令的-r标志,否则最终会删除文件夹。它不是用于删除多个文件,而是用于递归删除,读取你的男人。
  • 您在链接阶段使用了$<而不是$^,因此您的可执行文件会遗漏许多目标文件。

解决意见: GNU make有许多预定义的规则,函数和变量,你应该在使用它们之前使用它们。它具有编译和链接C和C++程序的基本规则,这就是为什么你的Makefile不需要重新定义已存在的%.o: %c规则。 你可以在你最喜欢的shell中输入以下内容来查看所有这些:
$ make -p > predefined.mk
$(RM)$(CC)是这些预定义变量之一,您可以自己查看它们实际包含的内容。 现在,由于许多用户都关注头文件依赖关系,让我们来解决这个问题。您不必手动执行此操作,GCC和Clang等现代编译器会在您设置完成后为您执行此操作。 每个.c文件的依赖关系将在.d文件中生成,该文件必须包含在Makefile中。 要告诉编译器在编译时生成这些文件,您需要传递一个预处理器标志:
CPPFLAGS := -MMD
现在依赖项是自动生成的,我们需要包含它们:
DEP := $(OBJ:.o=.d)
-include $(DEP)
你也想清理它们:
clean:
    $(RM) $(APP) $(OBJ) $(DEP)
现在你的Makefile看起来像这样:
APP             := app
SRC             := $(wildcard src/*.c)
OBJ             := $(SRC:.c=.o)
DEP             := $(OBJ:.o=.d)
CPPFLAGS        := -MMD
CFLAGS          := -W -Wall -g -std=c99 -pedantic
LDLIBS          := -lm
all: $(OBJS)
$(APP): $(OBJ)
    $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
clean:
    $(RM) $(APP) $(OBJ) $(DEP)
-include $(DEP)
最后一点:语法$(SRC:.c=.o)$(SRC:%.c=%.o)的快捷方式,它也是$(patsubst %.c,%.o,$(SRC))的快捷方式。