首页 文章

如果编译器选项与先前使用的选项不同,则使目标过时

提问于
浏览
2

请考虑以下目录树,以了解要解决的问题的虚构玩具示例:

- Makefile
- lib.c
+ foo/
   |-- config.mk
+ bar/
   |-- config.mk
  • foo/config.mk 的内容:
CFLAGS += -DFOO
  • bar/config.mk 的内容:
CFLAGS += -DBAR
  • make 调用 make 作为目标 foobar 导致文件 foo/config.mkbar/config.mk 分别被包含(通过include directive),并且正在构建 lib.o ,即:
# build lib.o with the macro FOO defined
$ make foo

# build lib.o with the macro BAR defined
$ make bar

# build lib.o with both the macros FOO and BAR defined
$ make foo bar
$ make bar foo

构建 lib.o 的默认规则使用变量 COMPILE.c ,该变量是根据通过使用选项--print-data-base调用 make 获得的输出定义的:

COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

除其他事项外, COMPILE.c 的扩展取决于变量 CFLAGS 的值,而变量 CFLAGS 依赖于是否包含 foo/config.mkbar/config.mk ,因为这些makefile修改了 CFLAGS 变量 .


我想要实现的是,如果当前使用的变量 COMPILE.c 的扩展与用于上一次构建的 lib.o 的扩展不同,则将目标 lib.o 视为过时目标 . 例如:

$ make foo

# It shouldn't rebuild anything since lib.o should be up-to-date
$ make foo

# It should rebuild lib.o since it should be out-of-date
$ make bar

# It should rebuild lib.o since it is again out-of-date
$ make foo bar

# It shouldn't rebuild lib.o since it is up-to-date
$ make bar foo

This solution解释了到目前为止我是如何实现这种行为的 . 任何建议都受到欢迎 .

3 回答

  • 2

    我会将变量的值转储到另一个包含的makefile中,并检查当前值是否与包含的makefile中的值不同 . 就像是:

    ifeq ($(filter foo,$(MAKECMDGOALS)),foo)
    include foo/config.mk
    endif
    ifeq ($(filter bar,$(MAKECMDGOALS)),bar)
    include bar/config.mk
    endif
    -include old_compile.mk
    
    COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
    
    ifneq ($(COMPILE.c),$(OLD_COMPILE.c))
    FORCE := force
    endif
    
    lib.o: lib.c $(FORCE)
        $(COMPILE.c) $< -o $@
        echo 'OLD_COMPILE.c := $(COMPILE.c)' > old_compile.mk
    
    .PHONY: foo bar all force
    
    foo bar all: lib.o
    

    演示:

    $ make foo
    cc -DFOO   -c lib.c -o lib.o
    echo 'OLD_COMPILE.c := cc -DFOO   -c' > old_compile.mk
    $ make foo
    make: Nothing to be done for 'foo'.
    $ make foo bar
    cc -DFOO -DBAR   -c lib.c -o lib.o
    echo 'OLD_COMPILE.c := cc -DFOO -DBAR   -c' > old_compile.mk
    make: Nothing to be done for 'bar'.
    $ make bar foo
    make: Nothing to be done for 'bar'.
    make: Nothing to be done for 'foo'.
    
  • 1

    到目前为止,我的方法是生成一个文件 cc-options-dump ,每次构建 lib.o 时都会使用 COMPILE.c 变量的扩展内容 .

    将当前 COMPILE.c 变量的扩展产生的MD5哈希与用于先前构建的MD5哈希进行比较,即,其内容存储在文件 cc-options-dump 中的那个(如果有的话)(即,如果文件确实存在的话) .

    .PHONY: foo bar require-rebuild cc-options-dump
    
    # include the xxx/config.mk files
    # sort built-in function to make it independent of the order (i.e., "foo bar" or "bar foo")
    $(foreach opt,$(sort $(MAKECMDGOALS)),$(eval include $(opt)/config.mk))
    
    # obtain the MD5 hash of the previously used flags
    ifneq (,$(wildcard cc-options-dump))
    prev-cc-options-hash := $(shell md5sum cc-options-dump | cut -f1 -d' ')
    endif
    
    # obtain the MD5 hash of the current flags
    curr-cc-options-hash := $(shell echo "$(COMPILE.c)" | md5sum | cut -f1 -d' ')
    
    # Do these hashes differ?
    ifneq ($(prev-cc-options-hash),$(curr-cc-options-hash))
    # keep track of the fact that a rebuilt needs to be triggered
    does-need-rebuild := require-rebuild
    # just for displaying information
    $(info Rebuild required)
    else
    $(info Rebuild not required)
    endif
    
    # make both targets foo and bar dependent on the file with the flags
    # so that it has to be generated, since it is a phony target as well 
    foo bar: cc-options-dump
    
    # lib.o has now an additional prerequisite for determining whether it need to be rebuilt
    lib.o: $(does-need-rebuild)
    
    # save the used compiler flags for comparing them with the future flags
    cc-options-dump: lib.o
        @echo '$(COMPILE.c)' >$@
    

    对于目标 foo 和/或 bar 调用 make 时,此makefile的行为对应于所需的对应:

    $ make foo
    Rebuild required
    cc -DFOO   -c -o lib.o lib.c
    
    $ make foo
    Rebuild not required
    
    $ make bar
    Rebuild required
    cc -DBAR   -c -o lib.o lib.c
    
    $ make bar
    Rebuild not required
    
    $ make foo bar
    Rebuild required
    cc -DBAR -DFOO   -c -o lib.o lib.c
    
    $ make bar foo
    Rebuild not required
    

    使用sort内置函数对于上述最后两种情况正常工作至关重要 .

    如果有人能够提供更优雅的解决方案,那就太棒了 .

  • 0

    这是我的粗略模型,它直接在文件名中编码编译器/链接器标志 . 这只是一个想法,实际的实现应该更加健壮 .

    empty:=
    space:= $(empty) $(empty)
    comma:= ,
    
    LDFLAGS_NS=$(subst $(space),$(comma),$(LDFLAGS))
    CFLAGS_NS=$(subst $(space),$(comma),$(CFLAGS))
    
    EXEDIR=./test-exedir-cflags=$(CFLAGS_NS)-ldflags=$(LDFLAGS_NS)
    EXEDIR_PAT=./test-exedir-*
    OBJDIR=./test-objdir-cflags=$(CFLAGS_NS)
    OBJDIR_PAT=./test-objdir-*
    
    test.exe: $(EXEDIR)/test.exe
        rm -f test
        ln -s $< $@
    
    $(EXEDIR)/test.exe: test.o
        rm -rf $(EXEDIR_PAT)
        mkdir $(EXEDIR)
        cc $(LDFLAGS) -o $@ $<
    
    test.o: $(OBJDIR)/test.o
        rm -f test.o
        ln -s $< $@
    
    $(OBJDIR)/test.o: test.c
        rm -rf $(OBJDIR_PAT)
        mkdir $(OBJDIR)
        cc -c $(CFLAGS) -o $@ $<
    

    使用hello world test.c文件和这些命令进行测试:

    make test.exe
    make test.exe
    make test.exe CFLAGS="-g -Wall"
    make test.exe CFLAGS="-g -Wall"
    make test.exe
    

相关问题