Makefile-一次编译多个C文件 [英] Makefile - compile multiple C file at once

查看:254
本文介绍了Makefile-一次编译多个C文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题与 makefiles上的问题不同-在以下位置编译所有c文件在我有一个额外要求的意义上,一次:我想将所有目标文件重定向到一个单独的目录中.

This question is different from the one at makefiles - compile all c files at once in the sense that I have one extra requirement: I want to redirect all the object files in a separate directory.

这是设置:

我的目录中有多个来源,例如src/mylib.
我希望对象文件以build/mylib结尾.
另请注意,在mylib下有子目录.

I have multiple sources in a directory say src/mylib.
I want the objects files to end up in build/mylib.
Please note also that under mylib there are subdirectories.

第一次尝试如下:

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, $^)的值表明,即使我有多个文件,也只考虑了一个文件.

For the makefile above, only one object file was being generated. I guessed this might have something to do with GCC only being able to generate one object file at a time. Regardless of that, checking the values of $@ and $(word 2, $^) in the $(objects) target shows that only one file is being considered even though I have multiple files.

所以我将我的makefile更改为以下内容:

So I changed my makefile to the following:

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的另一个目的:仅重新编译那些自上次编译以来已更改的源文件.

The second makefile works as expected but objects files are being rebuilt again which defeats another purpose of using make: only recompile those source files that changed from the last compilation.

因此,我的问题是:如何一次在单独的目录中生成所有目标文件(这是指按照一条规则执行所有源文件的编译),同时确保是否有源文件没有更改,不应重新生成关联的对象文件.

Hence my question: how does one generate all object files in a separate directory at once (by this I mean perform the compilation of all sources files in one rule) while making sure that if a source file didn't change the associated object file should not be regenerated.

我不是在加快编译速度.我要寻找的是一条规则,该规则将生成所有目标文件,以便仅重新编译更新的源文件.

最后一个makefile可以完成工作,但是会重新编译所有源文件,这违背了使用make的另一个目的:只应重新编译更改的源文件.

The last makefile does the job but there is a recompiling of all source files which defeats another purpose of using make: only changed source files should be recompiled.

阅读评论后,看来我没有正确表达我的问题.由于我已经掌握了详细信息,因此我将在下面的其他详细信息中保留这个问题.

After reading comments, it appears I have not phrased my question properly. As the details of what I have are already present, I leave the question as it is with additional details below.

上面的源代码中的第二个生成文件有效.但这只能完成一半的工作. build目录有效地镜像src目录.
因此,如果我说一个文件为src/mylib/point/point.c,则会生成build/mylib/point/point.o.这是第一部分.
第二部分是,如果point.c 进行更改,则必须重新生成build/mylib/point/目录中的point.o.但是在检查了目标文件上的时间戳后,我可以知道在再次运行make之后,新的目标文件替换了旧的目标文件.这不好,因为对于大型项目,编译时间保持O(n),而n是要编译的源文件的数量.

The second makefile in the source code above does work. But it does only half the job. The build directory effectively mirrors the src directory.
So if I have say a file as src/mylib/point/point.c, I get build/mylib/point/point.o generated. This is the first part.
The second part is that if point.c does not changes, point.o in the build/mylib/point/ directory must not be regenerated. But after checking timestamps on the object file, I can tell that a new object file replaced the old one after running make again. This is not good because for large projects, compilation time remains O(n) with n being the number of source files to compile.

所以这个问题是关于如何在不make重新生成目标文件的情况下保留第二个makefile.
从我可以从评论中收集到的信息来看,我对make的要求过多.但是,如果有人知道如何实现这一目标,那么我将悬而未决.

So this question is about how to preserve the second makefile without make regenerating object files.
From what I can gather from comments, I am asking too much from make. But if anyone knows how to make this happen, I leave the question open.

推荐答案

我终于有一些时间对此进行了试验,所以我想出了以下内容:

I finally had some time to experiment with this, so here is what I came up with:

BUILD_DIR = build
SRC_DIR = src
SOURCES = $(shell find $(SRC_DIR)/ -name "*.c")
TARGET  = program
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)

default: $(TARGET)

.SECONDEXPANSION:

$(OBJECTS) : $$(patsubst $(BUILD_DIR)/%.o,$(SRC_DIR)/%.c,$$@)
        mkdir -p $(@D)
        $(CC) -c -o $@ $(CFLAGS) $<

$(TARGET): $(OBJECTS)
        $(CC) -o $@ $(CFLAGS) $^

.PHONY: default

景点:

  • 我不得不将源代码find模式从".c"更改为"*.c",我不确定它是否取决于所使用的确切外壳,但是如果您想保持便携性,请确保使用广泛接受的模式.

  • I had to change the sources find pattern from ".c" to "*.c", I'm not sure if it depends on the exact shell used, but if you want to stay portable, be sure to use a widely accepted pattern.

.SECONDEXPANSION: 需要启用GNU Make的$$规则.需要在$(OBJECTS)的前提条件中允许基于目标的替换规则.

The .SECONDEXPANSION: is needed to enable the $$ rules for GNU Make. It is needed to allow target based substitution rules in the prerequisites for the $(OBJECTS).

先决条件$$(patsubst $(BUILD_DIR)/%.o,$(SRC_DIR)/%.c,$$@)是说 current 目标取决于具有相同文件夹结构和名称的特定源文件.

The prerequisite $$(patsubst $(BUILD_DIR)/%.o,$(SRC_DIR)/%.c,$$@) is saying, that the current target depends on a specific source file with the same folder structure and name.

命令mkdir -p $(@D)确保,如果缺少当前目标,则创建该路径.

The command mkdir -p $(@D) is ensuring, that the path of the current target is created if it's missing.

这篇关于Makefile-一次编译多个C文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆