下面的Makefile应该构建一个简单的项目
CC = gcc
INCDIRS = -Iinclude
CFLAGS = -Wno-implicit-function-declaration -g $(INCDIRS)
CFILES = char_list.c int_list.c main.c
OBJFILES = char_list.o int_list.o main.o
BINARY = main
BINDIR = bin/
OBJDIR = obj/
all: $(BINARY)
$(BINARY): $(OBJFILES)
$(CC) -o $(TARGETDIR)$@ $(OBJDIR)$^
%.o: %.c
$(CC) $(CFLAGS) -c -o $(OBJDIR)$@ $^
clean:
@rm -rf $(BINARY) $(OBJFILES)
我刚刚开始第一次使用Makefile,所以在尝试更改项目文件夹结构并使其与Makefile一起工作时遇到了问题(文件夹结构在最后)。当我构建二进制文件时,规则要求所有.o,如图所示,但是因为我想更好地组织我的工作空间,所以我选择将所有.o文件放在obj文件夹中。(OBJDIR)会将其应用于每个OBJFILE,但我现在知道这不会发生,因为$(OBJDIR)和$^被类似地处理为字符串,所以这个命令只是将两者连接起来。
我怎样才能使OBJFILES中的每个单词都有正确的前缀?
.
├── bin
├── char_list.c
├── include
│ ├── char_list.h
│ └── int_list.h
├── int_list.c
├── main.c
├── Makefile
├── obj
│ ├── char_list.o
│ ├── int_list.o
│ └── main.o
└── parser_pseudo.txt
这就是错误,正如所说,只有第一个。o得到前缀
gcc -Wno-implicit-function-declaration -g -Iinclude -c -o obj/char_list.o char_list.c
gcc -Wno-implicit-function-declaration -g -Iinclude -c -o obj/int_list.o int_list.c
gcc -Wno-implicit-function-declaration -g -Iinclude -c -o obj/main.o main.c
gcc -o main obj/char_list.o int_list.o main.o
/usr/bin/ld: cannot find int_list.o: No such file or directory
/usr/bin/ld: cannot find main.o: No such file or directory
collect2: error: ld returned 1 exit status
make: *** [Makefile:16: main] Error 1
1条答案
按热度按时间fcg9iug31#
首先,Makefile只做字符串连接。所以当你这样做时:
它将打印以下内容:
为了向列表添加前缀,可以像这样使用
$(addprefix prefix,names...)
:但是第二,你的规则的目标和依赖项不再准确,因为你在命令中改变了输出文件的路径,但没有调整规则的其余部分。所以一般来说,我建议你在变量中已经有了完整的文件的实际路径。为了保存大量的输入和重复,你可以使用
$(patsubst pattern,replacement,words...)
:假设您实际上希望所有C文件都在项目根目录中,那么您也可以使用
$(wildcard *.c)
,而不是手动列出C文件。我在这里做了两个修改:
1.使用
:=
而不是=
。这意味着在赋值时而不是使用时计算右侧。对于常规的=
,如果您在Makefile中写入$(OBJFILES)
20次,则$(patsubst ...)
将执行20次,而:=
只执行一次。IMO你应该在你定义的变量是最终的,在整个构建过程中任何时候都不会改变,并且在定义之前没有引用的时候使用它。.PHONY: all
的使用。由于./all
不是将被创建的文件,因此您应该通知make
情况是这样的。从这里开始,我将使用.PHONY: all clean
。我想做的另一个改变是文件夹的创建和删除。原因很简单,就是git。如果你在一个git项目中创建了一个空文件夹
obj/
,那么你就没有办法提交它。你可以在里面创建一个占位符文件,但那很难看。相反,你可以修改%.o
规则为$(OBJDIR)/%.o
,并通过在末尾追加| $(OBJDIR)
来添加一个仅订单依赖。然后你需要的就是一个构建规则,如下所示:现在,既然你已经在Makefile中声明了
BINDIR
,但在构建规则中使用了$(TARGETDIR)
,我假设你也希望你的二进制文件进入一个文件夹。所以总而言之,我会像这样重写你的Makefile:注意:Stack Overflow的Markdown渲染器会将所有制表符转换为空格,因此如果您复制任何内容,请确保将它们转换回来。