【问题标题】:Makefile compiles all the files everytimeMakefile 每次都会编译所有文件
【发布时间】:2019-02-03 19:31:39
【问题描述】:

我的 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

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

我知道这可能是问题所在。由于没有创建 display.o、logic.o 和 man.o 文件,因此 MAKE 始终遵守与其关联的规则。那么我如何告诉它检查实际的 .o 文件名 1.o、文件名 2.o 等以获取时间戳,并且仅当它们比相应的 c 文件和 h 文件甚至它们所依赖的 lib 文件更旧时才重新编译。

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

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

【问题讨论】:

  • 你有什么问题?你在做什么,你期望发生什么,实际发生了什么?
  • 对不起,我以为标题很清楚。我的问题是我的 Makefile 每次运行时都会编译所有文件。我不希望这种情况发生。另外,正如我帖子的最后一行所述,我希望将所有 .o 文件保存在文件夹 obj 下。那么我该怎么做这两件事呢?

标签: gcc makefile compilation


【解决方案1】:

在基本层面上,make 正在寻找类似的行:

target: dependency
    command

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

值得注意的是,只有在 (a) dependency 不存在,或 (b) dependencytarget 更新时,才会调用 dependency 的规则。

在问题中,假设我们运行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

【讨论】:

  • 我知道我需要以某种方式创建依赖项。但是网上提供的解决方案似乎都不适用于我的 Makefile。您提供的第一个选项意味着我应该手动查看每个 c 文件并查找其头文件,对吗?有没有更简单的选择?因为我的c文件太多了。第二个选项上的链接说它不适用于 Windows 操作系统。并且只讲编译规则,不讲如何创建依赖。
  • 不,它说可用的规则可能不同。 make -p%o: %.c 输出了什么?例如,没有什么能阻止你添加(字面上)gen/display/*.c 作为display.o: 的依赖项。
  • 没有找到那个特定的选项。但是发现了这个:# Not a target: .C.o: # Implicit rule search has not been done. # 修改时间从未检查过。 # 文件尚未更新。 # 要执行的命令(内置): $(COMPILE.C) $(OUTPUT_OPTION) $
  • 我刚刚注意到您的 .o 规则不是实际文件。 GNU make 有 .PHONY` 目标 - gnu.org/software/make/manual/html_node/Phony-Targets.html - 为此
  • 所以我尝试添加 gen/display/*.c 作为 display.o: 的依赖项,如下所示: display.o: gen/display/*.c gcc -Igen/display -Igen/ logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 -Llib -c 这也没有帮助
【解决方案2】:

问题是您的二进制对象目标(如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 $@ $^

但原则保持不变。

【讨论】:

  • 那么如何避免 Makefile 每次都编译所有内容呢?我知道我需要创建依赖项。尝试了许多解决方案,但似乎没有任何帮助。
  • 我添加了一些更实用的解释,最后一段应该有助于将其转化为您的情况。
  • 但是我在原始帖子中提到的许多不同文件夹下有许多 c 文件。所以尝试上述解决方案意味着我为每个 c 文件编写规则,这很乏味。是否有不同的解决方案来自动化依赖关系?
  • 所以我尝试了 - display: gen/display/*.c gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 - Llib -o $@ -c 但出现以下错误:gcc.exe:致命错误:无法指定 -o 与 -c、-S 或 -E 与多个文件编译终止。 make: *** [显示] 错误 1
  • 是的,无论如何,您将无法在单次编译中从所有先决条件中汇总构建目标。当:之前列出了多个目标时,这并不意味着它们都是一次构建的,而是相同的规则适用于所有目标,并且需要我们每次调用它们。
猜你喜欢
  • 2019-08-16
  • 1970-01-01
  • 2021-02-25
  • 2020-06-04
  • 1970-01-01
  • 1970-01-01
  • 2011-05-29
  • 1970-01-01
  • 2013-05-20
相关资源
最近更新 更多