删除文件后如何确保`make`重建所有对象

时间:2017-02-26 10:51:43

标签: makefile

考虑以下Makefile骨架:

HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))

executable: $(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $(OBJECTS)

%.o: %.c
    rm -f $@
    $(CC) $(CFLAGS) -c -o $@ $< || { rm -f $@; exit 1; }

$(OBJECTS): Makefile $(HEADERS)

如果修改了以下任何文件,这些规则可确保重新编译OBJECTSexecutable

  • 声明文件(* .h)
  • 实施文件(* .c)
  • Makefile本身

这很好用。它还介绍了将新的源代码文件添加到项目目录的情况(假设新文件未使用cp -amv添加)。没有涉及的案例是删除文件。

删除时重新编译很有用,因为它会捕获剩余源代码中的剩余部分,因为它会从executable中删除多余的数据。

从项目中删除源代码文件后,确保make命令重建所有对象的简洁有效方法是什么?

答案可以通过$(shell ...)使用任何常见的Linux命令。

4 个答案:

答案 0 :(得分:1)

您可以创建一个列表文件(名称listfile),其中包含标题和C来源列表。如果您从源代码树中删除文件,则需要重新创建此listfilelistfile应该是Makefile中的依赖项。

HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))

executable: $(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $(OBJECTS)

%.o: %.c
    rm -f $@
    $(CC) $(CFLAGS) -c -o $@ $< || { rm -f $@; exit 1; }

genlist:
    find . -name \*.c -or -name \*.h > listfile

$(OBJECTS): Makefile $(HEADERS) listfile

.PHONY: genlist

删除后,您应该运行make genlist

当然你可以扩展这个想法:你将创建一个列表文件,每个make将生成一个临时列表文件(例如使用mktemp)并与&#34;官方& #34; listfile。如果他们不同将覆盖&#34;官方&#34; listfile - 删除后不必运行make genlist

答案 1 :(得分:1)

处理此问题的一种方法是使用现代自动生成的依赖项方法,例如described here。这些方法具有内置属性,可以正确处理已删除的文件。

如果您不想这样做,那么类似于@uzsolt建议的内容就是您需要做的事情,但如果您想避免需要明确地运行make genlist,则必须发挥作用在make实现文件丢失之前:

HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))

executable: $(OBJECTS)
        $(CC) $(CFLAGS) -o $@ $(OBJECTS)

%.o: %.c
        rm -f $@
        $(CC) $(CFLAGS) -c -o $@ $< || { rm -f $@; exit 1; }

$(OBJECTS): Makefile $(HEADERS) sourcelist

sourcelist: FORCE
        @for f in $(SOURCES) $(HEADERS); do echo "$$f"; done > $@.tmp
        @[ `comm -23 $@ $@.tmp | wc -l` -eq 0 ] || mv $@.tmp $@

FORCE:

这里的想法是您将旧列表与新列表进行比较,并且只有在旧列表包含新列表不包含的内容时才修改列表。这确保除非删除某些内容,否则sourcelist的时间戳不会更改,因此它不会强制对象文件过期。

答案 2 :(得分:1)

MadScientist提供的回复非常好(特别是使用现代自动生成的依赖方法)。

如果现代自动生成的依赖项方法不是一个选项,则使用echocmp的替代方法是使用make使include处理依赖项}和$(file ...)(注意$(OBJECTS)的额外依赖关系):

HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))

executable: $(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $(OBJECTS)

%.o: %.c
    rm -f $@
    $(CC) $(CFLAGS) -c -o $@ $< || { rm -f $@; exit 1; }

$(OBJECTS): Makefile $(HEADERS) deps.mk

include deps.mk

deps.mk: $(HEADERS) $(SOURCES)
    $(file >$@,$@: $(HEADERS) $(SOURCES))
    $(file >>$@,$(HEADERS) $(SOURCES):)

这只是一个例子,行为的任何组合都是可能的。例如,如果您的要求是:

  • 重新编译所有内容
    • 创建或修改头文件;或
    • 删除标题或源文件
  • 否则,只重新编译添加或修改的源文件 - 如果有的话

您可以使用MISSING功能捕获filter-out中已删除文件的列表:

HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))

executable: $(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $(OBJECTS)

%.o: %.c
    rm -f $@
    $(CC) $(CFLAGS) -c -o $@ $< || { rm -f $@; exit 1; }

include missing.mk
ifneq (,$(MISSING))
$(MISSING):
endif

$(OBJECTS): Makefile $(HEADERS) $(MISSING)

missing.mk: $(MISSING) $(HEADERS) $(SOURCES)
    $(file >$@,MISSING=$$(filter-out $$(HEADERS) $$(SOURCES),$(HEADERS) $(SOURCES)))

答案 3 :(得分:1)

每个人都建议生成辅助makefile。我非常支持这些建议。但是我更喜欢保持这个makefile的隔离生成。以下是将其保存在单个$(shell ...)命令中的解决方案:

THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))

$(if $(shell find -name '*.h' -or -name '*.c' | \
    awk '\
        /.*\.h$$/ { h=h " " $$0 } \
        /.*\.c$$/ { c=c " " $$0 } \
        END { \
          print "HEADERS :=" h; \
          print "SOURCES :=" c \
        }' > $(THIS_MAKEFILE).include.new && \
    diff -q $(THIS_MAKEFILE).include.new $(THIS_MAKEFILE).include 2> /dev/null || mv $(THIS_MAKEFILE).include.new $(THIS_MAKEFILE).include),)

include $(THIS_MAKEFILE).include
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))

executable: $(OBJECTS); $(CC) $(CFLAGS) -o $@ $(OBJECTS)

%.o: %.c; rm -f $@ && $(CC) $(CFLAGS) -c -o $@ $< || { rm -f $@; exit 1; }

$(OBJECTS): $(THIS_MAKEFILE) $(THIS_MAKEFILE).include $(HEADERS)

$(shell)包含在$(if)中以禁止其输出。 这个解决方案有一个$(shell)调用而不是两个,并且不依赖于相对较新的$(文件)GNU Make函数。

适合小型项目,但不能很好地扩展。