【问题标题】:Makefile: why makefile with % does not work?Makefile:为什么带有 % 的 makefile 不起作用?
【发布时间】:2019-05-16 08:40:56
【问题描述】:

我的 makefile 中的% 不起作用。

我已经在 ubuntu 16.04 x64 上测试了 makefile。

我的 makefile 代码是: 版本 1

CC=gcc
OBJ=main.o
TARGET:=main
.PHONY: clean
all : main
# ${OBJ}:%.o:%.c

%.i : %.c
        $(info Preprocess: build main.i)
        ${CC} -E -o $@ $<

%.s : %.i
        $(info Compile: build main.s)
        ${CC} -S -o $@ $<

%.o : %.s
        $(info Assemble: build main.o)
        ${CC} -c -o $@ $<

main : main.o
        $(info Link: build main.o)
        ${CC} -o $@ $^

clean:
        rm -f *.o *.out *.s *.i *.asm *.map ${OBJ} main

运行make,终端打印信息:

gcc    -c -o main.o main.c
Link: build main.o
gcc -o main main.o

所以只有最后一条规则(main: main.o) 运行。 第一步是自动派生代码(gcc -c -o main.o main.c)。 为什么其他规则没有运行?

然后我修改第三条规则,添加静态模式: 第 2 版

...
%.i : %.c
        $(info Preprocess: build main.i)
        ${CC} -E -o $@ $<

%.s : %.i
        $(info Compile: build main.s)
        ${CC} -S -o $@ $<

main.o : %.o : %.s
        $(info Assemble: build main.o)
        ${CC} -c -o $@ $<

%: %.o
        $(info Link: build main.o)
        ${CC} -o $@ $^

然后所有规则生效,显示信息:

Preprocess: build main.i
gcc -E -o main.i main.c
Compile: build main.s
gcc -S -o main.s main.i
Assemble: build main.o
gcc -c -o main.o main.s
Link: build main.o
gcc -o main main.o
rm main.i

(为什么要运行“rm main.i”?)

我再次修改makefile: 第 3 版

%.o:%.c
        $(info build main.o)
        ${CC} -c -o $@ $<
main : main.o
        $(info Link: build main.o)
        ${CC} -o $@ $^

可以正常运行。打印信息:

build main.o
gcc -c -o main.o main.c
Link: build main.o
gcc -o main main.o

那么,为什么版本1不能正常工作?

【问题讨论】:

    标签: c linux makefile


    【解决方案1】:

    它不起作用,因为 make 知道如何从 .c 源构建目标文件 (.o),是 built-in implicit rule

    您可以禁用隐式规则,如果您使用 make -r 运行版本 1,它应该会按预期运行。

    .i文件被删除是因为是中间文件,默认make删除所有中间文件,你可以使用.PRECIOUS: some-file-name避免这种情况

    % makefile 中的规则称为 stem模式规则(不是 通配符,这是另一回事)

    您可以使用 --debug--debug=all 参数运行 make 以获得详细日志或更详细的日志

    编辑

    您还有两个选项可以禁用内置规则并让版本 1 正常工作:

    • 用空规则覆盖特定的内置规则,只需添加%.o: %.c
    • 禁用所有添加空后缀列表的内置规则.SUFFIXES:

    如果您修改后缀列表,唯一有效的预定义后缀规则将是那些由您指定的列表中的一个或两个后缀命名的规则

    编辑

    禁用我过去使用的内置规则的附加选项:

    MAKEFLAGS += --no-builtin-rules
    

    【讨论】:

    • 好答案。 OP 还应注意模式规则特定于 GNU make。当然,如果他们不关心与其他实现的兼容性,那很好,但人们应该始终了解何时依赖于标准工具的扩展。
    • 谢谢,所以隐式规则的优先级高于模式规则(没有显式对象),我认为需要显式对象的静态模式才能避免隐式规则生效。所以我用静态模式重写了makefile。
    • @Edward 我不知道是否是优先事项,但肯定make 认为是一个选项,如果您有一个.s 文件存在,则可能会使用规则%.o:%.s(我没有检查)
    【解决方案2】:

    应该需要带有显式对象的静态模式以避免隐式规则生效。所以我用静态模式重写了makefile。它可以正常工作并且不运行“rm main.i”。

    CC:=gcc
    SRCS:=          $(wildcard *.c)
    OBJ:=           $(patsubst %.c, %.o, ${SRCS})
    PREFILE:=       $(patsubst %.o, %.i, ${OBJ})
    ASMFILE:=       $(patsubst %.o, %.s, ${OBJ})
    TARGET:=main
    all: ${TARGET}
    .PHONY: clean distclean
    
    $(PREFILE):%.i:%.c
            $(info Preprocess: build main.i)
            $(CC) -E -o $@ $<
    
    $(ASMFILE):%.s:%.i
            $(info Compile: build main.s)
            $(CC) -S -o $@ $<
    
    $(OBJ):%.o:%.s
            $(info Assemble: build main.o)
            $(CC) -c -o $@ $<
            @objdump -DrwC -Mintel $@ > $(patsubst %.o,%.o.asm,$@)
    
    $(TARGET):$(OBJ)
            $(info Link: build main)
            $(CC) -o $@ $^ -Wl,-Map=gcc.map
            @objdump -D $@ > $(patsubst %,%.asm,$@)
    
    clean:
            rm -f *.o *.out *.s *.i *.asm *.map ${OBJ} ${TARGET}
    
    distclean : clean
            rm -f *.d
    

    【讨论】:

    • 看起来不错,我在一个旧项目中经常使用茎模式,我不记得有这个选项,但也许我当时只是想念它,如果你需要复古兼容性,你可能想检查一下自此功能可用时起
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多