Makefile无法为简单项目创建单独的对象文件夹

时间:2016-12-26 15:09:49

标签: c makefile

我正在使用此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并将所有目标文件放入其中?

2 个答案:

答案 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 $@

编辑2

在发布正确的规则后,我在$(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这个问题的范围。