我有一个项目,其中包含src/
目录及其子目录(例如src/foo/
和src/bar/
)中的源,以及obj
目录中的对象和匹配的子目录(例如obj/foo/
和obj/bar/
)。
我使用以下(smimplified)Makefile
:
SOURCES=$(shell find src/ -type f -name '*.c')
OBJECTS=$(patsubst src/%.c,obj/%.o,$(SOURCES))
all: $(OBJECTS)
obj/%.o: src/%.c
gcc -c $< -o $@
问题是,如果obj/
或其中一个子目录不存在,我会收到以下错误:
Fatal error: can't create obj/foo/f1.o: No such file or directory
如何判断%.o
文件是否依赖于其包含目录的创建?
One solution使用“仅订购先决条件”:
$(OBJECTS): | obj
obj:
mkdir $@
但是,这仅针对obj/
解决了问题,而不是obj/foo
和obj/bar/
。我想过使用$(@D)
,但我不知道如何将所有这些结合在一起。
我还在每个目录中使用了隐藏的标记文件,但这只是一个黑客攻击,而且我还在GCC命令之前放了一个mkdir -p
,但这也感觉很乱。我宁愿避免使用递归的makefile,如果这是一个潜在的解决方案。
要创建一个类似于我的最小项目,您可以运行:
mkdir /tmp/makefile-test
cd /tmp/makefile-test
mkdir src/ src/foo/ src/bar/
echo "int main() { return 0; }" > src/main.c
touch src/foo/f1.c src/bar/b1.c src/bar/b2.c
答案 0 :(得分:1)
我不知道你为什么考虑在每个编译器操作之前添加mkdir -p
为“hacky”;这可能就是我要做的。但是,如果您不介意所有创建的目录,也可以这样做:
首先,您应该使用:=
来分配shell变量,而不是=
。前者远效率更高。其次,一旦有了文件名列表,就可以轻松计算目录列表。试试这个:
SOURCES := $(shell find src/ -type f -name '*.c')
OBJECTS := $(patsubst src/%.c,obj/%.o,$(SOURCES))
# Compute the obj directories
OBJDIRS := $(sort $(dir $(OBJECTS))
# Create all the obj directories
__dummy := $(shell mkdir -p $(OBJDIRS))
如果你真的想要只在对象即将创建时创建目录,那么你将不得不使用第二次扩展(未经测试):
SOURCES := $(shell find src/ -type f -name '*.c')
OBJECTS := $(patsubst src/%.c,obj/%.o,$(SOURCES))
# Compute the obj directories
OBJDIRS := $(sort $(dir $(OBJECTS))
.SECONDEXPANSION:
obj/%.o : src/%.c | $$(@D)
$(CC) -c $< -o $@
$(OBJDIRS):
mkdir -p $@
答案 1 :(得分:1)
我这样做:
SOURCES=$(shell find src -type f -name '*.c') # corrected small error
...
obj/%.o: src/%.c
if [ ! -d $(dir $@) ]; then mkdir -p $(dir $@); fi
gcc -c $< -o $@