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

时间:2015-06-16 09:28:06

标签: c makefile

我正在尝试为我的项目编写一个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.o等。

1 个答案:

答案 0 :(得分:3)

看来你在这里混淆了一些东西。

它们基本上是你需要在这里使用的两种规则,它们都有相同的语法:

targets : prerequisites
    recipe

当你这样写:

$(APP): $(OBJS)
    $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)

您说要创建$(APP),要做到这一点,您需要$(OBJS)才能存在或创建。

现在你写这个:

$(OBJS): $(SRC) $(INCLUDES)
    $(CC) $(CFLAGS) $(IFLAGS) -c $< -o $@

您告诉您要创建.o个文件列表,以及您需要的所有$(SRC)$(INCLUDES)个别文件。

由于您在配方中使用$<,这是先决条件列表中的第一个条目的快捷方式,因此您始终会使用相同的源文件进行编译

要做你想做的事,你必须抽象一些东西并告诉make&#34;这就是我希望你建立依赖于相应的任何 .o文件的方式.c&#34 ;.这是模式规则的工作:

%.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,以便最终选择一个

  • 您根本不需要列出头文件。不要打扰。

  • 不要小心使用-r命令的rm标记,您最终会删除文件夹。它不是用于删除多个文件,而是用于递归删除,读取你的男人。

  • 您在链接阶段使用了$<而不是$^,因此您的可执行文件会遗漏许多目标文件。

解决意见:

GNU make有许多预定义的规则,函数和变量,你应该在使用它们之前使用它们。它具有编译和链接C和C ++程序的基本规则,这就是为什么你的Makefile不需要重新定义已经存在的%.o: %c规则。

您可以在自己喜欢的shell中输入以下内容来查看所有这些内容:

$ make -p > predefined.mk

$(RM)$(CC)是这些预定义变量之一,您可以自己查看它们实际包含的内容。

现在,由于许多用户都关注头文件依赖关系,所以让我们解决这个问题。您不必手动执行此操作,GCC和Clang等现代编译器会在您设置完成后为您执行此操作。

每个.c文件的依赖关系将在必须包含在Makefile中的.d文件中生成。

要告诉编译器在编译时生成这些文件,您需要传递一个预处理器标志:

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))的快捷方式。