【发布时间】:2017-01-10 12:08:00
【问题描述】:
这个问题与makefiles - compile all c files at once 的问题不同,因为我有一个额外的要求:我想将所有目标文件重定向到一个单独的目录中。
设置如下:
我在一个目录中有多个来源,比如src/mylib。
我希望对象文件以 build/mylib 结尾。
另请注意,mylib 下有子目录。
第一次尝试如下:
sources = $(shell find src/ -name ".c")
objects_dirs = $(subst src/, build/, $(dir $(sources)) # This variable is used by the build rule to create directories for objects files prior to compilation
objects = $(subst src/, build/, $(patsubst %.c, %.o, $(sources))) # This variable has the paths to the objects files that will be generated in the build directory
# This is where things aren't working as expected
$(objects): build $(sources)
$(cc) $(cflags) -o $@ $(word 2, $^))
build:
$(foreach dir, $(objects_dirs), $(shell mkdir -p $(dir)))
对于上面的 makefile,只生成了一个目标文件。我猜这可能与 GCC 一次只能生成一个目标文件有关。不管怎样,检查$(objects) 目标中$@ 和$(word 2, $^) 的值表明即使我有多个文件,也只考虑一个文件。
所以我将我的 makefile 更改为以下内容:
sources = $(shell find src/ -name ".c")
objects = $(subst src/, build/, $(patsubst %.c, %.o, $(sources))) # This variable has the paths to the objects files that will be generated in the build directory
# This works as expected but it appears to me like make is generating all the objects files even though source files did not change. This can be seen by checking the timestamps on new object files after running make again.
$(objects): build $(sources)
$(foreach source, $(sources), $(shell $(cc) $(cflags) -o $(subst src/,build/, $(patsubst %.o,%.c,$(source))) $(source)))
build:
$(foreach dir, $(objects_dirs), $(shell mkdir -p $(dir)))
第二个 makefile 按预期工作,但对象文件正在重新构建,这违背了使用 make 的另一个目的:只重新编译那些从上次编译中更改的源文件。
因此我的问题是:如何在一个单独的目录中生成所有目标文件一次(我的意思是在一个规则中执行所有源文件的编译)同时确保如果一个源文件未更改,不应重新生成关联的目标文件。
我不是在加快编译速度。我寻求的是一种规则,它将生成所有对象文件,以便只重新编译更新的源文件。
最后一个 makefile 完成了这项工作,但需要重新编译 所有源文件,这违背了使用 make 的另一个目的:只应重新编译更改的源文件。
编辑
阅读 cmets 后,我似乎没有正确表达我的问题。由于我已经提供了详细信息,因此我将问题保留原样,并在下面提供更多详细信息。
上面源代码中的第二个makefile可以工作。但它只完成了一半的工作。 build 目录有效地反映了src 目录。
所以如果我说一个文件为src/mylib/point/point.c,我会生成build/mylib/point/point.o。这是第一部分。
第二部分是如果point.c没有发生变化,则build/mylib/point/目录下的point.o一定不能重新生成。但是在检查目标文件上的时间戳后,我可以看出在再次运行make 后,一个新的目标文件替换了旧的目标文件。这不好,因为对于大型项目,编译时间仍然是O(n),n 是要编译的源文件的数量。
所以这个问题是关于如何在没有make 重新生成目标文件的情况下保留第二个 makefile。
从我从 cmets 收集到的信息来看,我对 make 的要求太多了。但是,如果有人知道如何做到这一点,我会留下这个问题。
【问题讨论】:
-
为什么不使用
-j选项运行并行make? -
@Someprogrammerdude 我不熟悉
-j标志,但我上次使用它的印象是它加快了编译速度,这不是我的问题。 -
为什么要一次性创建它们?通常 make 为每个对象调用编译器并一个接一个地创建它。如果不加快速度,您的请求的目的是什么?
-
在不查看您文件的详细信息的情况下,我几乎可以确定
-j就是您想要的。 makefile 应该简单地正确列出所有依赖项(不要太多,也不要太少:完全是现有的)。然后-j <n>选项将开始并行构建尽可能多的可用目标,最大为。 (编辑:我看到您可能希望在一次 gcc 运行中构建它们。这当然是 -j未解决的不同要求。) -
核心问题:你想在源码树的不同深度构建源码文件;好的。这很难做到,违背了
make的原则,make不会让这变得容易。您可能想阅读Recursive make considered harmful(和其他地方),尽管我认为米勒有些夸大了这种方法的问题,而递归 make 是 I 将如何处理这个问题。 简答:对此没有简答。 (即,您可能需要重新考虑您的要求)