C一次性通过makefile实现文件

时间:2017-11-05 01:53:49

标签: c vim makefile header-files

我正在研究项目,我使用几个.c和.h文件 我创建了Makefile,其中我根据所有这些文件的变化实现了可执行程序 问题是,当我使用make时,程序被编译,但是当我执行程序时,它运行时没有任何变化。我需要保存(在vim中工作:w)所有包含的文件,即使我只更改了一个文件 如果我不保存所有这些文件,程序将被编译,但执行与更改之前相同的操作 有人可以解释我为什么会这样吗?

Makefile代码:

CC=gcc  
CFLAGS=-WALL 

execFile: execFile.o functions.h newDataTypes.h  

谢谢。

3 个答案:

答案 0 :(得分:3)

您没有更新execFile的原因是因为您没有更新它。或者至少你似乎不在这种特殊情况下。

有很多方法可以做到这一点。但是,由于您使用gcc并且我假设您正在使用gnu,因此以下可能是您可以执行 1 的最佳解决方案。

鉴于文件:


-rw-r--r-- 1 user sudo  73 Nov  4 22:54 exeFile.c
-rw-r--r-- 1 user sudo  74 Nov  4 22:54 exeFile.h
-rw-r--r-- 1 user sudo  90 Nov  4 22:55 hello_world.c
-rw-r--r-- 1 user sudo 888 Nov  4 23:03 Makefile

  

cat exeFile.c

#include <stdio.h>
#include "exeFile.h"

int main()
{
  hello_world();
}
  

exeFile.h

#ifndef _EXEFILE_H
#define _EXEFILE_H

extern void hello_world();

#endif
  

hello_world.c

#include <stdio.h>
#include "exeFile.h"

void hello_world()
{
  printf("Hello World\n");
}

您可以设置生成依赖项的make文件,并确保程序始终正确编译:

CC=gcc
CFLAGS=-Wall
SOURCES=exeFile.c hello_world.c
EXE=exeFile
OBJ=$(SOURCES:%.c=%.o)

DEPDIR := .deps
$(shell mkdir -p $(DEPDIR) >/dev/null)
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td


COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
COMPILE.cc = $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
POSTCOMPILE = @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d && touch $@

%.o: %.c
%.o: %.c $(DEPDIR)/%.d
    $(COMPILE.c) $(OUTPUT_OPTION) $<
    $(POSTCOMPILE)

%.o : %.cc
%.o : %.cc $(DEPDIR)/%.d
    $(COMPILE.cc) $(OUTPUT_OPTION) $<
    $(POSTCOMPILE)

%.o : %.cxx
%.o : %.cxx $(DEPDIR)/%.d
    $(COMPILE.cc) $(OUTPUT_OPTION) $<
    $(POSTCOMPILE)

$(DEPDIR)/%.d: ;
.PRECIOUS: $(DEPDIR)/%.d

$(EXE): $(OBJ)
    $(CC) -o $@ $(OBJ) $(LDFLAGS)

clean:
    $(RM) $(OBJ) $(EXE)

dev-clean: clean
    $(RM) -r $(DEPDIR)

include $(wildcard $(patsubst %,$(DEPDIR)/%.d,$(basename $(SOURCES))))

让我们回顾有关依赖关系的相关部分

DEPDIR =

此实现将依赖项文件放入名为.deps

的子目录中

$(shell mkdir -p $(DEPDIR) 2>/dev/null)

GCC不会为输出创建子目录,此行确保DEPDIR目录始终存在。

DEPFLAGS = ...

这些是GCC特定的标志,告诉编译器生成依赖信息。

-MT $@

在生成的依赖项文件中设置目标的名称。

-MMD

除了编译之外还生成依赖性信息。 -MMD忽略生成的依赖项中的系统标头:如果您希望将系统标头保留为先决条件,请改用-MD

-MP

为列表中的每个先决条件添加一个make目标,这可以避免在删除文件时出错。

-MF $(DEPDIR)/$*.Td

将生成的依赖项文件写入临时文件$(DEPDIR)/$*.Td,例如hello_world.c将生成hello_world.Td作为临时依赖内容,以便在Makefile中使用。

POSTCOMPILE = ...

首先将生成的临时依赖项文件重命名为真实依赖项文件。我们在单独的步骤中执行此操作以侧向编译错误。接下来我们明确触摸文件以避免gcc错误。

%.o : %.c

从.c文件中删除构建目标文件的内置规则,以便使用我们的规则。对其他内置规则也这样做。

... $(DEPDIR)/%.d

将生成的依赖项文件声明为目标的先决条件,以便在缺少目标时重建目标。

$(DEPDIR)/%.d: ;

使用空配方创建模式规则,以便在依赖文件不存在时make不会失败。

.PRECIOUS: $(DEPDIR)/%.d

标记要创建的相关性文件,因此它们不会自动删除为中间文件。

include ...

包含存在的依赖项文件:将SOURCES中列出的每个文件转换为其依赖项文件。使用通配符可以避免在不存在的文件上失败。

1 See Auto-Dependencies Generation了解详情。

答案 1 :(得分:0)

<强>修正:

告诉make可执行文件仅依赖于目标文件,而目标文件取决于头文件:

execFile: execFile.o
execFile.o: functions.h newDataTypes.h

<强>解释

为了构建可执行文件,需要两个步骤:

  • 编译C源文件(包括头文件)以生成目标文件,
  • 链接目标文件以生成可执行文件。

因此,在更改头文件时,必须重新编译,即重新构建目标文件,而不仅仅是重新链接,从而生成相同目标文件中的相同可执行文件。

在Makefile中,未正确定义依赖项。您告诉make,当您的头文件发生更改时,应重建目标文件execFile.o)。相反,你告诉它应该重建可执行文件execFile)。

答案 2 :(得分:0)

首先,您的依赖项是错误的。您的可执行文件不依赖于.h头文件,因为它们仅在编译时使用。依赖关系通常在.o个文件和.h文件之间,因为当您修改一个.h文件时,必须编译包含.c文件以生成.o文件文件。如果您有execFile.o(由于缺少完整信息,我认为这取决于execFile.c#include s functions.hnewDataTypes.h ,规则应该是:

execFile.o: execFile.c functions.h newDataTypes.h

正如其他回复中指出的那样,没有必要编写命令来构建.o文件,因为有这样的默认规则:

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

(观察编译器有一个-c选项,指示只编译而不链接,我们将在下面返回)这意味着一旦检测到.o已过时(如依赖于.c.h标记)它将使用上面的命令编译,结果是:

gcc -Wall -o execFile.o -c execFile.c

进行适当的编辑。

其他的是可执行文件的依赖项。必须包括这些,因为make(1)不知道哪个目标文件构成了最终的可执行文件。在这种情况下,假设您的计划execFile取决于execFile.oa.ob.oc.o,我通常会写:

execFile_objs = execFile.o a.o b.o c.o
execFile: $(execFile_objs)
     $(CC) $(LDFLAGS) -o $@ $(execFile_objs)

所以.o中的任何一个被更改(因为源文件中的间接更改)整个程序再次链接(但只有被触摸的文件被编译)

如果你有一个Makefile创建一个只有一个源文件和几个包含文件的程序,那么每次都可以编译您以这种方式修改一个源文件:

execFile: execFile.c functions.h newDataTypes.h
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ execFile.c

这将执行

gcc -Wall  -o execFile execFile.c
  1. 注意此命令中没有-c(仅编译,不链接)选项。
  2. 没有提及包含文件(由于代码中的#include指令而包含它们...并且您只在此声明可执行文件也依赖(并且必须构建)以防任何.h文件已被修改。
  3. 自动依赖规则起初有点令人困惑,因为它们会让你认为有这样的规则可以从任何其他类型的文件制作任何类型的文件(嗯,有.c -> .o个文件,{ {1}}直接编译为可执行文件)通常,当您的目标依赖于比自动规则状态更多的文件时,您必须包含依赖项。在这种情况下,非常重要不包含任何命令,因此编译器选择自动规则(当您不包含生成目标的命令时,.c -> <nothing>程序会尝试对它使用默认规则,或者如果你没有包含命令则什么都没有,它只假设你的依赖关系是通过这个假目标间接的 - 并且,因为这个假目标不是在这个过程中构建的,它将永远失败并被遵循)