【问题标题】:Make: Compiling only one .c file though i modify the header file which is included in two .c files [duplicate]制作:尽管我修改了两个 .c 文件中包含的头文件,但只编译了一个 .c 文件[重复]
【发布时间】:2019-04-23 23:00:52
【问题描述】:

我有两个 .c 文件和一个包含在两个 .c 文件中的 .h 文件。

我有制作文件:

CC=gcc
CFLAGS=-I.

OBJ = hellofunc.o hellomake.o

DEPS := $(OBJ:.o=.d)

-include $(DEPS)

%.o: %.c
     $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
     $(CC) -c -o $@ $< $(CFLAGS)

hellomake: hellomake.o hellofunc.o
    $(CC) -o $@ $^

我修改了 hellomake.h 头文件,然后在 make 文件之上运行。它只编译分配给 DEPS 变量的第一个文件,即 hellofunc.c

仅供参考,当我更改 DEPS 变量的顺序时,它正在编译 hellomake.o ,似乎 make 只选择分配给 DEPS 变量的第一个文件。 我的makefile有什么问题吗..请帮忙。

【问题讨论】:

  • 你没有声明对hellomake.h的任何依赖,所以Make当然会忽略它。
  • @tripleee,您能否通过更新我的 make 文件来简要回答一下?我是新手
  • 我使用 include 指令同时包含 hellomake.d 和 hellofunc.d 文件。应该可以吗?
  • 我认为这个问题不应该被标记为与投票结束的人引用的问题的重复。这个利用了自动依赖生成。不需要手动将头文件添加到先决条件。

标签: makefile gnu-make


【解决方案1】:

正如@tripleee 在 cmets 中指出的那样,make 默认会构建它遇到的第一个目标。由于直接包含include 文件,就好像它们被剪切并粘贴到位一样,第一个.d 文件(我猜你会发现它是hellofunc.c)中的第一个目标是make 遇到的第一个目标,这就是 make 的目标。

如果您将include 行移至文件末尾,则文件中的第一个目标将是hellomake,因此make 将默认尝试构建目标。

元注释:如果可以的话,我会说最好避免这种依赖.d 文件的模式,而是直接在makefile 中“手动”表达足够多的依赖关系。这样做.d 方式确实有效(即,我并不是说你所做的是错误的)并且似乎可以节省劳动力,但根据我的经验,它往往有点脆弱,部分原因是如果你不这样做没有.d 文件可手,然后你突然零依赖。为了获得.d 文件,您必须将它们签入您的代码存储库(您正在使用存储库,不是吗?),但这意味着它们'会经常过时,而且......它可能会变得有点混乱。

【讨论】:

  • 如果一个.c文件包含n个头文件。我们不能在我们的make文件中指定它们对吗?看起来有点难看。有什么简单的方法可以处理吗?
  • 我不同意上面最后一段中关于自动生成的先决条件的讨论。如果正确完成,它们实际上是完全可靠的(不幸的是,因为 make 是一个通用工具,而不是专门用于处理 C/C++ 程序的工具,正确完成需要一些工作)。在简单的情况下手动处理先决条件是可以的,但在任何中型到大型程序中都是站不住脚的。请参阅make.mad-scientist.net/papers/… 了解如何正确执行自动依赖的讨论(即使所有 .d 文件都已删除!)
  • @MadScientist 感谢相反的观点和有用的链接。 “一些工作”和该页面的长度说明了我认为的缺点,以及为什么我认为这种方法“脆弱”(根据我的经验)而不是错误的。多年来,我尝试了多种管理此问题的方法,无论是否使用 automake(我会仔细阅读您的页面),并得出结论,make 是“大部分正确且可读/可维护”优于的系统之一'完全正确但只写'。这最终可能是风格/品味问题。
  • 如果您有 10 个具有简单先决条件的文件,则可以手动列出内容。在我从事的项目中,我们在许多目录中有超过 1500 个源文件和超过 1700 个头文件(这远非一个“大”项目,真的)。它是 C++,所以有 lot 包含递归头文件,根本无法手动管理。真正的问题是,如果您忘记在 makefile 中添加先决条件,那么您的程序可能会以微妙且难以理解的方式中断。这不仅仅是一个编译错误。一个不可靠的 makefile 通常比没有 makefile 更糟糕。
【解决方案2】:

编辑:考虑MadScientist 的评论和blog post 关于.d 文件的单独配方。

您可以告诉 make 如何使用单独的规则生成 .d 文件,而不是将其放在另一个配方中(但请参阅上面提到的博客文章,有几个不这样做的原因)。

你可能应该告诉 make 你的默认目标是hellomake

.DEFAULT_GOAL := hellomake

CC=gcc
CFLAGS=-I.

OBJ = hellofunc.o hellomake.o

DEPS := $(OBJ:.o=.d)

-include $(DEPS)

%.d: %.c
     $(CC) $(CFLAGS) -MM -MT $@ -MF $@ $<

%.o: %.c
     $(CC) -c -o $@ $< $(CFLAGS)

hellomake: hellomake.o hellofunc.o
    $(CC) -o $@ $^

如果你让 make 找到源文件并推断其余部分可能会更好:

.DEFAULT_GOAL := hellomake

CC     := gcc
CFLAGS := -I.
SRCS   := $(wildcard *.c)
OBJS   := $(patsubst %.c,%.o,$(SRC))
DEPS   := $(patsubst %.c,%.d,$(SRC))

-include $(DEPS)

%.d: %.c
     $(CC) $(CFLAGS) -MM -MT $@ -MF $@ $<

%.o: %.c
     $(CC) -c -o $@ $< $(CFLAGS)

hellomake: $(OBJS)
    $(CC) -o $@ $^

最后,按照MadScientist's advices,一个更好、更高效、更不容易出错的解决方案可能是:

.DEFAULT_GOAL := hellomake

CC     := gcc
CFLAGS := -I.
SRCS   := $(wildcard *.c)
OBJS   := $(patsubst %.c,%.o,$(SRC))
DEPS   := $(wildcard $(patsubst %.c,%.d,$(SRC)))

include $(DEPS)

%.o: %.c %.d
    $(CC) -MT $@ -MMD -MP -MF $*.Td $(CFLAGS) -c -o $@ $<
    mv -f $*.Td $*.d && touch $@

hellomake: $(OBJS)
    $(CC) -o $@ $^

%.d: ;
.PRECIOUS: %.d

【讨论】:

  • 哦,知道了。谢谢!我们有关于“-MM -MT”依赖选项的教程或文章吗?有的话可以分享一下吗?
  • 实际上为.d 文件创建一个单独的配方会导致很多令人不快的副作用。创建必备文件的新现代(也是最好的)方法是将它们创建为构建目标文件的一部分,而不是在单独的规则中。请参阅我在上面 cmets 中的博客文章以获得完整的讨论。
  • 顺便说一句,它没有用。我在上面复制了相同的 make 文件内容。它仍然只编译一个 .c 文件。
  • @santosh:你确定这两个文件都包含这个头文件吗?您是否检查了.d 文件的内容以验证该头文件是否确实被列为依赖项?
  • 是的,我验证了。我在两个 .c 文件中都包含头文件。 .d 文件中也列出了头文件。
猜你喜欢
  • 2021-05-03
  • 1970-01-01
  • 2021-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-13
  • 1970-01-01
  • 2021-03-16
相关资源
最近更新 更多