首页 文章

如何让Makefile自动重建包含已修改头文件的源文件? (在C / C中)

提问于
浏览
82

我有以下makefile用于构建我正在处理的程序(实际上是内核) . 它从头开始,我正在学习这个过程,所以它并不完美,但我认为它在这一点上足够强大,因为我在编写makefile时的经验水平 .

AS  =   nasm
CC  =   gcc
LD  =   ld

TARGET      =   core
BUILD       =   build
SOURCES     =   source
INCLUDE     =   include
ASM         =   assembly

VPATH = $(SOURCES)

CFLAGS  =   -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
            -nostdinc -fno-builtin -I $(INCLUDE)
ASFLAGS =   -f elf

#CFILES     =   core.c consoleio.c system.c
CFILES      =   $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
SFILES      =   assembly/start.asm

SOBJS   =   $(SFILES:.asm=.o)
COBJS   =   $(CFILES:.c=.o)
OBJS    =   $(SOBJS) $(COBJS)

build : $(TARGET).img

$(TARGET).img : $(TARGET).elf
    c:/python26/python.exe concat.py stage1 stage2 pad.bin core.elf floppy.img

$(TARGET).elf : $(OBJS)
    $(LD) -T link.ld -o $@ $^

$(SOBJS) : $(SFILES)
    $(AS) $(ASFLAGS) $< -o $@

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

#Clean Script - Should clear out all .o files everywhere and all that.
clean:
    -del *.img
    -del *.o
    -del assembly\*.o
    -del core.elf

我对makefile的主要问题是,当我修改一个或多个C文件包含的头文件时,不会重建C文件 . 通过让我的所有头文件都是我所有C文件的依赖项,我可以很容易地解决这个问题,但是这会在我更改/添加头文件时有效地导致项目的完全重建,这不会非常优雅 .

我想要的只是包含我改变的头文件的C文件重建,以及整个项目再次链接 . 我可以通过使所有头文件成为目标的依赖项来进行链接,但是当我们包含的头文件较新时,我无法弄清楚如何使C文件失效 .

我听说GCC有一些命令可以使这成为可能(因此makefile可以以某种方式找出需要重建的文件)但我不能在我的生活中找到一个实际的实现示例来查看 . 有人可以发布一个解决方案,在makefile中启用此行为吗?

编辑:我应该澄清,我熟悉将各个目标放入并拥有每个目标的概念.o需要头文件 . 这要求我每次在某处包含头文件时都要编辑makefile,这有点痛苦 . 我正在寻找一个可以自己派生头文件依赖的解决方案,我相当肯定我已经在其他项目中看到了 .

9 回答

  • 16

    正如本网站其他地方已经指出的那样,请参阅此页:http://make.paulandlesley.org/autodep.html

    简而言之,gcc可以自动为您创建.d依赖文件,这些文件是包含您编译的.c文件的依赖项的迷你makefile片段 . 每次更改.c文件并进行编译时,都会更新.d文件 .

    除了向gcc添加-M标志外,还需要在makefile中包含.d文件(如上面的Chris所述) . 在页面中有一些更复杂的问题,使用sed解决,但你可以忽略它们并做“清理”以清除.d文件,只要抱怨无法生成不再存在的头文件 .

  • 3

    您可以像其他人所说的那样添加'make depend'命令,但为什么不让gcc创建依赖项并同时编译:

    DEPS := $(COBJS:.o=.d)
    
    -include $(DEPS)
    
    %.o: %.c
        $(CC) -c $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<
    

    '-MF'参数指定用于存储依赖项的文件 .

    '-include'开头的破折号告诉Make在.d文件不存在时继续(例如在第一次编译时) .

    注意gcc中似乎存在关于-o选项的错误 . 如果将对象文件名设置为 obj/_file__c.o ,则生成的 _file_.d 仍将包含 _file_.o ,而不是 obj/_file_c.o .

  • 7

    这相当于Chris Dodd's answer,但使用了不同的命名约定(巧合的是不需要 sed magic . 从a later duplicate复制 .


    如果您使用的是GNU编译器,编译器可以为您组装一个依赖项列表 . Makefile片段:

    depend: .depend
    
    .depend: $(SOURCES)
            rm -f ./.depend
            $(CC) $(CFLAGS) -MM $^>>./.depend;
    
    include .depend
    

    还有工具 makedepend ,但我从来没有像 gcc -MM 那样喜欢它

  • 3

    您必须为每个C文件创建单独的目标,然后将头文件列为依赖项 . 您仍然可以使用通用目标,之后只需放置 .h 依赖项,如下所示:

    %.o: %.c
            @echo Compiling $<...
            $(CC) $(CFLAGS) -c -o $@ $<
    
    foo.c: bar.h
    # And so on...
    
  • 0

    除了@mipadi所说的,你还可以探索使用' -M ' option to generate a record of the dependencies. You might even generate those into a separate file (perhaps ' depend.mk ') which you then include in the makefile. Or you can find a ' make depend '规则来编辑带有正确依赖关系的makefile(Google术语:"do not remove this line"和depend) .

  • 20

    基本上,您需要动态创建makefile规则,以便在头文件更改时重建目标文件 . 如果你使用gcc和gnumake,这很容易;只是说:

    $(OBJDIR)/%.d: %.c
            $(CC) -MM -MG $(CPPFLAGS) $< | sed -e 's,^\([^:]*\)\.o[ ]*:,$(@D)/\1.o $(@D)/\1.d:,' >$@
    
    ifneq ($(MAKECMDGOALS),clean)
    include $(SRCS:%.c=$(OBJDIR)/%.d)
    endif
    

    在你的makefile中 .

  • 0

    这些答案都不适合我 . 例如 . Martin Fido的回答表明gcc可以创建依赖文件,但是当我尝试它为我生成空(零字节)对象文件时没有任何警告或错误 . 它可能是一个gcc bug . 我在

    $ gcc --version gcc(GCC)4.4.7 20120313(Red Hat 4.4.7-16)

    所以这是我完整的Makefile,对我有用;它是解决方案的组合其他人没有提到的东西(例如“后缀替换规则”指定为.cc.o :):

    CC = g++
    CFLAGS = -Wall -g -std=c++0x
    INCLUDES = -I./includes/
    
    # LFLAGS = -L../lib
    # LIBS = -lmylib -lm
    
    # List of all source files
    SRCS = main.cc cache.cc
    
    # Object files defined from source files
    OBJS = $(SRCS:.cc=.o)
    
    # # define the executable file 
    MAIN = cache_test
    
    #List of non-file based targets:
    .PHONY: depend clean all
    
    ##  .DEFAULT_GOAL := all
    
    # List of dependencies defined from list of object files
    DEPS := $(OBJS:.o=.d)
    
    all: $(MAIN)
    
    -include $(DEPS)
    
    $(MAIN): $(OBJS)
        $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS)
    
    #suffix replacement rule for building .o's from .cc's
    #build dependency files first, second line actually compiles into .o
    .cc.o:
        $(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $<
        $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
    
    clean:
        $(RM) *.o *~ $(MAIN) *.d
    

    注意我使用.cc ..上面的Makefile很容易调整.c文件 .

    同样重要的是要注意这两条线的重要性:

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

    所以首先调用gcc来构建一个依赖文件,然后实际编译一个.cc文件 . 等等每个源文件 .

  • 27

    更简单的解决方案:只需使用Makefile使.c到.o编译规则依赖于头文件以及项目中作为依赖项的任何其他相关内容 .

    例如,在Makefile中的某个地方:

    DEPENDENCIES=mydefs.h yourdefs.h Makefile GameOfThrones.S07E01.mkv
    
    ::: (your other Makefile statements like rules 
    :::  for constructing executables or libraries)
    
    # Compile any .c to the corresponding .o file:
    %.o: %.c $(DEPENDENCIES)
            $(CC) $(CFLAGS) -c -o $@ $<
    
  • -1

    我相信 mkdep 命令就是你想要的 . 它实际上会扫描.c文件中的 #include 行并为它们创建依赖关系树 . 我相信Automake / Autoconf项目默认使用它 .

相关问题