Makefile每次都会编译所有文件

时间:2019-02-03 19:31:39

标签: gcc makefile compilation

我的Makefile每次运行时都会编译所有文件,尽管这些文件没有更改。我知道这个问题已经问过几次了,但是所提供的解决方案似乎都不适合我。我是Makefile的新手,大多数时候我不了解解决方案中使用的行话。另外,我想将所有生成的.o文件保存在“ obj”文件夹下

这是我的文件夹结构

project (-)
        gen (-)
            display (-)
                .c and .h files
            logic (-)
                .c and .h files
        lib (-)
            include (-)
                .h files
            .lib files  
        man (-)
            .c and .h files
        obj (-)
            want to save all the .o files here

我正在使用MinGW在Windows操作系统上运行它

这是我的Makefile:

ALL: demo

SRCS:= filename1.o filename2.o filename3.o filename4.o and so on till filename27.o

demo: display.o logic.o man.o
    gcc $(SRCS) -lglut32 -loglx -lopengl32 -Llib -o demo

display.o:
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 -Llib -c

logic.o:
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/logic/*.c -lglut32 -loglx -lopengl32 -Llib -c

man.o:
    gcc -Igen/display -Igen/logic -Iman -Ilib/include man/*.c -lglut32 -loglx -lopengl32 -Llib -c

clean:
    @echo "Cleaning up.."
    -rm -rf *.o
    -rm *.exe

注意:lib文件夹中包含glut和oglx文件。 Display.o,lib.o和man.o没有相应的.c文件。它们只是其中包含许多c文件的文件夹名称。

我知道这可能是问题所在。由于没有创建display.o,logic.o和man.o文件,因此MAKE遵守与其传送时间相关的规则。因此,我如何告诉它检查时间戳的实际.o filename1.o,filename2.o等,并仅在它们早于相应的c文件和h文件(甚至是它们依赖的lib文件)之前重新编译。 >

我尝试了以下方法来创建依赖关系,并避免每次都编译文件。但这没有帮助。

%.d: %.c
     @set -e; rm -f $@; \
     $(CC) -M $(CFLAGS) $< > $@.$$$$; \
     sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
     rm -f $@.$$$$

2 个答案:

答案 0 :(得分:1)

从根本上讲,make正在寻找以下行:

target: dependency
    command

如果target不存在,它将调用dependency的规则,然后运行command。如果确实存在target,它将测试dependency是否较新或不存在。如果是这样,它将调用dependency的规则,然后运行command。否则,它将停止。

重要的是,只有在(a)dependency不存在,或者(b)dependencydependency更新时,才会调用target的规则。 / p>

在问题中,假设我们运行make demo。然后make查找以demo:开头的行,并注意它声明了依赖项。因此,它依次查看每个依赖项,以查看他们是否需要采取措施。它首先发现display.o。它注意到display.o:不存在,因此它运行关联的规则。其他*.o也是如此。

为避免*.o规则由于不存在关联文件而始终运行,可以像这样重写:

ALL: demo

SRCS:= filename1.o filename2.o filename3.o filename4.o and so on till filename27.o

demo: display.ts logic.ts man.ts
    gcc $(SRCS) -lglut32 -loglx -lopengl32 -Llib -o demo

display.ts: gen/display/*.c
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 -Llib -c
    echo . > display.ts

logic.ts: gen/logic/*.c
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/logic/*.c -lglut32 -loglx -lopengl32 -Llib -c
    echo . > logic.ts

man.ts: man/*.c
    gcc -Igen/display -Igen/logic -Iman -Ilib/include man/*.c -lglut32 -loglx -lopengl32 -Llib -c
    echo . > man.ts

clean:
    @echo "Cleaning up.."
    -rm -rf *.o *.ts
    -rm *.exe

答案 1 :(得分:1)

问题是您的二进制对象目标(如display.o)实际上与它们的规则所产生的文件不匹配。如果您告诉make它需要作为目标display.o,则它(通常是假目标除外,但总是重新运行)期望该规则的配方生成相应的文件,并且可以跟踪该文件是否目标需要重新制定。如果没有生成此类文件,则此目标始终被评估为过时且需要重新构建。

下面这棵树是一个愚蠢的例子:

.
├── Makefile
├── main.c
└── test
    └── file.c

和Makefile:

main: test.o main.o
    $(CC) -o main *.o

test.o:
    $(CC) $(CFLAGX) -c test/*.c

没有test.o文件,需要重新设置目标……规则运行,再次产生file.o。由于这个目标已经过重制,并且是main的前提,所以……一切都会被重制。

现在进行一下小的修改:

main: test.o main.o
    $(CC) -o main *.o

test.o:
    $(CC) $(CFLAGX) -o $@ -c test/*.c

test.o目标确实会生成test.o文件,并且如果test.c不变,并且test.o不变且main.c同样,我们得到:

$ make
make: 'main' is up to date.

它仍然不完全正确,因为它应该读为:

main: test.o main.o
    $(CC) -o main $+

test.o: test/*.c
    $(CC) $(CFLAGX) -o $@ -c $^

我在其中声明test.o的先决条件,并通过规则配方中的自动变量来引用它们和目标。同样,链接也需要先决条件。当然,在这个简单的示例中,我可以依靠隐式模式规则来做到这一点:

main: test/file.o main.c

test/file.o: test/*.c

这对您的Makefile意味着什么?编译目标文件时,请查看它们实际产生什么并将目标与之匹配,或者(例如,以-o $@告诉它们,以产生与目标完全匹配的文件。


我已经扩展了这个愚蠢的示例,test/中现在有两个文件:

.
├── Makefile
├── main.c
└── test
    ├── file.c
    └── other.c

Makefile可能如下所示:

main: obj/file.o obj/other.o main.c

obj/%.o: test/%.c
    mkdir -p obj
    $(CC) $(CFLAGS) -c -o $@ $^

它现在将目标文件存储在obj/中,并且仍然了解什么需要什么并可以跟踪更改。当然,您的设置会更复杂,并且需要更多规则,也许还会从目录树中划分出实际来源或中间目标,并定义一些变量来处理该信息,例如:

OBJS := $(patsubst test/%.c,obj/%.o,$(wildcard test/*.c))

main: $(OBJS) main.c

obj/%.o: test/%.c
        mkdir -p obj
        $(CC) $(CFLAGS) -c -o $@ $^

但是原理保持不变。