是什么原因导致以下 Makefile 中的模式规则之间发生冲突?

What causes a conflict between pattern rules in the Makefile below?

提问人:Roland Tóbiás 提问时间:11/8/2023 最后编辑:Roland Tóbiás 更新时间:11/8/2023 访问量:32

问:

将以下 Makefile 视为 MWE:

###################################################################
# first bunch
###################################################################
FORTRAN1 = ifort -c
PATH1 = sub/
FILE1 = first.f90
SOURCE1 = $(FILE_TEST:%=$(PATH1)%)
OBJECT1 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE1))))
###################################################################
#second bunch
###################################################################
FORTRAN2 = ifort -traceback -c
PATH2 = sub/
FILE2 = second.f90
SOURCE2 = $(FILE2:%=$(PATH2)%)
OBJECT2 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE2))))
###################################################################
# first rule
###################################################################
all: $(OBJECT1)
lnk/%.o: $(PATH1)%.f90
   $(FORTRAN1) -o $@ $<
###################################################################
# second rule
###################################################################
all: $(OBJECT2)
lnk/%.o: $(PATH2)%.f90
   $(FORTRAN2) -o $@ $<

当我运行此脚本时,将执行以下命令:

ifort -traceback -c -o lnk/first.o sub/first.f90
ifort -traceback -c -o lnk/second.o sub/second.f90

换句话说,第一条和第二条规则都使用 ,而第一条规则应该使用 .你能帮帮我是什么导致了这种奇怪的行为吗?我怎样才能实现第一条规则应该使用的?FORTRAN2=ifort -traceback -cFORTRAN1=ifort -c$(FORTRAN1)$

编辑#1:

按照MadScientist的建议,我尝试了以下方法:

###################################################################
# first bunch
###################################################################
FORTRAN1 = ifort -c
PATH1 = sub/
FILE1 = first.f90
SOURCE1 = $(FILE_TEST:%=$(PATH1)%)
OBJECT1 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE1))))
###################################################################
#second bunch
###################################################################
FORTRAN2 = ifort -traceback -c
PATH2 = sub/
FILE2 = second.f90
SOURCE2 = $(FILE2:%=$(PATH2)%)
OBJECT2 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE2))))
###################################################################
# first rule
###################################################################
$(OBJECT1): lnk/%.o: $(PATH1)%.f90
   $(FORTRAN1) -o $@ $<
###################################################################
# second rule
###################################################################
$(OBJECT2): lnk/%.o: $(PATH2)%.f90
   $(FORTRAN2) -o $@ $<

但是,它只执行一个(即第一个)命令:

ifort -c -o lnk/first.o sub/first.f90

编辑#2:

问题是我删除了目标。正确的文件内容应为:all:

###################################################################
# first bunch
###################################################################
FORTRAN1 = ifort -c
PATH1 = sub/
FILE1 = first.f90
SOURCE1 = $(FILE_TEST:%=$(PATH1)%)
OBJECT1 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE1))))
###################################################################
#second bunch
###################################################################
FORTRAN2 = ifort -traceback -c
PATH2 = sub/
FILE2 = second.f90
SOURCE2 = $(FILE2:%=$(PATH2)%)
OBJECT2 = $(addprefix lnk/,$(addsuffix .o,$(basename $(FILE2))))
###################################################################
# first rule
###################################################################
all: $(OBJECT1)
$(OBJECT1): lnk/%.o: $(PATH1)%.f90
   $(FORTRAN1) -o $@ $<
###################################################################
# second rule
###################################################################
all: $(OBJECT2)
$(OBJECT2): lnk/%.o: $(PATH2)%.f90
   $(FORTRAN2) -o $@ $<
makefile 编译 gnu-make

评论

1赞 John Bollinger 11/8/2023
有两种方法可以判断要构建的内容:(i) 在命令行上命名所需的目标;或 (ii) 如果命令行上未命名任何目标,则生成文件中名称不以 .除了这些目标之外,还将以递归方式构建它确定过时的任何先决条件。您正在查看 (ii)。将合成的 “” 目标作为文件的第一个目标包含在内是约定的,但不是强制性的,其先决条件是应在默认运行中构建的所有目标。makemake.makeall

答:

1赞 MadScientist 11/8/2023 #1

Make 不是一种脚本语言:make 不会读取 makefile 并在处理时“执行”它。Make 完全读取整个生成文件以及所有包含的文件,然后在完成后开始处理。

只能有一个模式规则具有相同的目标集和先决条件。在你的makefile中,你有:

PATH1 = sub/

PATH2 = sub/

lnk/%.o: $(PATH1)%.f90
   ...

lnk/%.o: $(PATH2)%.f90
   ...

由于 和 变量具有相同的值,因此 make 可以看到:PATH1PATH2

lnk/%.o: sub/%.f90
   ...

lnk/%.o: sub/%.f90
   ...

第二条规则与第一条规则相同,因此它会覆盖它,并且第一条规则会丢失。然后,在完成所有这些解析后,THEN make 开始尝试构建对象,并查看应该使用哪个规则来匹配哪个目标。由于只有一个模式规则匹配,因此它用于所有对象。

有几种不同的方法可以做到这一点。一种是使用静态模式规则而不是完整模式规则

$(OBJECT1) : lnk/%.o: $(PATH1)%.f90
   ...

$(OBJECT2) : lnk/%.o: $(PATH2)%.f90
   ...

现在,这些规则只匹配那些确切的对象。

评论

0赞 TobiR 11/8/2023
谢谢你的回答。也许我犯了一个错误,但这个解决方案只执行第一个编译命令(请参阅我编辑的问题)。
1赞 MadScientist 11/8/2023
为什么要删除目标?你仍然需要它。除非您在命令行上指定了一组目标,否则 make 将仅构建它在 makefile 中找到的第一个目标。在您的原始 makefile 中,它是 ,它依赖于所有对象。在第二个 makefile 中,您删除了目标,这意味着它找到的第一个目标是变量中的第一个对象文件,因此默认情况下,这就是它将构建的全部内容。all:makeallallOBJECT1
3赞 MadScientist 11/8/2023
我建议你阅读 GNU Make 手册,至少是介绍部分: gnu.org/software/make/manual/html_node/Introduction.html 这样可以省去你在 SO 和其他地方问很多问题。