我正在使用此Makefile Tutorial来了解如何使用Makefile。 这个问题可能与this thread重复,但我认为我需要更加明确。
我的项目结构:
--exercise_14/
--> ex14.c
--> ex14.h
--> main.c
--> Makefile
ex14.*
没有什么复杂的,只有简单的头文件有3个函数声明(ex14.h
),它们的实现(ex14.c
)和main.c
调用它们。
我的Makefile
如下:
CC = gcc
CFLAGS = -Wall -g
DEPS = ex14.h
ODIR=obj
_OBJ=ex14.o main.o
OBJ=$(patsubst %,$(ODIR)/%,$(_OBJ))
all: ex14
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o $@ $<
ex14: $(OBJ)
$(CC) $(CFLAGS) -o $@ $^
clean:
rm -f ex14 $(ODIR)*.o
rm -rf $(ODIR)
我目前正在了解文件中的patsubst
应如何运作以及每次运行时
make clean all
我明白了:
gcc -Wall -g -c -o obj/ex14.o ex14.c
Assembler messages:
Fatal error: can't create obj/ex14.o: No such file or directory
Makefile:16: recipe for target 'obj/ex14.o' failed
make: *** [obj/ex14.o] Error 1
有意义的是,没有创建obj/
文件夹,也没有找到ex14.o
进行进一步编译。一种方法是使用mkdir obj
,然后执行make
,但我想避免这种情况。
应该添加哪些行才能让我的Makefile
将文件夹设为ODIR=obj
并将所有目标文件放入其中?
答案 0 :(得分:1)
正确的解决方案是通过order-only dependency:
使目标文件依赖于他们的目录考虑一个示例,其中您的目标将放置在单独的目录中,并且在运行make之前该目录可能不存在。在这种情况下,您希望在将任何目标放入其中之前创建目录,但是,因为无论何时添加,删除或重命名文件时目录上的时间戳都会更改,我们当然不希望重建所有目标。目录的时间戳更改。管理此问题的一种方法是使用仅限订单的先决条件:使目录成为所有目标的仅限订单的先决条件:
$(ODIR)/%.o: %.c $(DEPS) | $(ODIR)
<same-original-recipe>
$(ODIR):
mkdir -p $@
答案 1 :(得分:0)
正如其他人评论的那样,你可以明确地将依赖关系放到$(ODIR)
目录中(必须在之前创建任何相关文件---编译):
$(OBJS): $(ODIR)
$(ODIR):
mkdir -p $@
这将确保您在任何相关文件(任何$(ODIR)
文件)之前创建了*.o
目录,并且只创建了一次。
Makefile的最终内容应如下:
CC = gcc
CFLAGS = -Wall -g
DEPS = ex14.h
ODIR=obj
_OBJ=ex14.o main.o
OBJ=$(patsubst %,$(ODIR)/%,$(_OBJ))
all: ex14
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o $@ $<
ex14: $(OBJ)
$(CC) $(CFLAGS) -o $@ $^
clean:
rm -f ex14 $(ODIR)*.o
rm -rf $(ODIR)
$(OBJ): $(ODIR)
$(ODIR):
mkdir -p $@
在发布正确的规则后,我在$(patsubst)
变量扩展中发现了一些错误,这使得在没有删除所有内容时失败。
以下是一个正确的,优化的Makefile:
$ cat Makefile
CC = gcc
CFLAGS = -Wall -g
DEPS = ex14.h
ODIR=obj
OBJS=ex14.o main.o
POBJS=$(foreach i,$(OBJS),$(ODIR)/$(i))
LIBS= # for example: -lm for math library.
.PHONY: all ex14 clean
$(ODIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
all: ex14
ex14: $(POBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(POBJS) $(LIBS)
clean:
rm -rf $(ODIR)
$(POBJS): $(ODIR) $(DEPS)
$(ODIR):
mkdir -p $@
将依赖关系{/ 1}}放在
中$(DEPS)
使$(OBJS): $(ODIR) $(DEPS)
文件的自动默认依赖关系规则有效,并且在makefile中不再需要。另外,我使用.c -> .o
前缀来表示P
中的{em> pathed file 对POBJS
(这是对象文件的普通列表)也是递归删除{{ 1}}子目录不需要首先删除目标文件,然后是子目录(只需要最后一个命令)另外,链接时,通常使用OBJS
传递它们来传递它们到链接器。最后,如果最后要包含一些库,只需使用objs
库(样本不需要)。
正如其中一条评论中所述,仅编辑一个源文件会触及$(LDFLAGS)
目录,并且所有其他文件都会过时,这将使所有文件都被删除一个刚编译成下次编译。这个问题没有解决方案,因为它存在循环依赖(对象依赖于存在的目录,并且每个编译都触及目录,这使得所有文件再次触发依赖关系在最后一个之前编译)这个问题的唯一可能解决方案是消除对目录的依赖并在调用$(LIBS)
之前手动构建它。
Berkeley make($(ODIR)
或make
,它取决于)通过允许您定义pmake
依赖项来解决此问题,该依赖项将在任何其他工作之前解决。您可以在那里创建目录。我认为GNU make没有这个功能:
bmake
但这不是针对GNU make这个问题的范围。