为子目录中的许多二进制文件制作文件

问题描述

我一直在尝试创建一个 makefile,它在文件树中采用多个源,并为每个源生成一个单独的二进制文件。 我当前的项目结构如下:

topdir/
       bin/
       [Other Directories]
       Gamma/
           Makefile
           5a5/
               prog1.5.f03
               prog2.5.f03
               .
               .
               .
           5a10/
               prog1.10.f03
               prog2.10.f03
               .
               .
               .
           5a15/
           .
           .
           .

我想为子目录中的每个源文件生成一个二进制文件,并将它们放在 bin/ 中。 以下是我当前的 Makefile:

#Compliler
FC=gfortran

#Directories for build and sources
BIN=topdir/bin
FROOT=topdir/Gamma
#Source files
SRCF =$(wildcard $(FROOT)/5a5/*.f03)
SRCF+=$(wildcard $(FROOT)/5a10/*.f03)
SRCF+=$(wildcard $(FROOT)/5a15/*.f03)
SRCF+=$(wildcard $(FROOT)/5a20/*.f03)
SRCF+=$(wildcard $(FROOT)/5a25/*.f03)
#Binary targets
EXES=$(addprefix $(BIN)/,$(notdir $(patsubst %.f03,%,$(SRCF))))

#Flag to place mods with binaries
FCFLAGS= -J $(BIN)

all: $(EXES)

$(EXES): $(SRCF)
        $(FC) $(FCFLAGS) -o $@ $<

这有效,但仅将 $(SRCF) 中的第一个文件编译到所有目标。我想找到一种方法来运行我的源列表并将一个目标与同名的一个源匹配。

我仔细研究了这三个其他问题以使我达到这一点,但我错过了最后一个让我达到目标的问题。

*C++ Single Makefile Multiple Binaries

*Compile multiple executables from multiple source directories to single bin directory using makefile

*Makefile: Compiling from directory to another directory

提前致谢!

解决方法

这个错误是我在 StackOverflow 上看到的最常见的错误之一。但我不知道如何阻止人们制作它。

$(EXES): $(SRCF)
        $(FC) $(FCFLAGS) -o $@ $<

人们看到这条规则后会做什么? make 实际上所做的与扩展任何其他规则时所做的事情相同:首先扩展变量,然后解释规则。

展开变量后,我们得到:

bin/prog1.5 bin/prog2.5 ... bin/prog1.10 bin/prog2.10 ... : topdir/Gamma/5a5/prog1.5.f03 topdir/Gamma/5a5/prog2.5.f03 ... topdir/Gamma/5a10/prog1.10.f03 topdir/Gamma/5a10/prog2.10.f03 ...
        $(FC) $(FCFLAGS) -o $@ $<

所以现在我们有一个规则,将所有可执行文件列为目标,将所有源文件列为先决条件。人们认为 make 会如何解释这一点?我猜他们希望 make 以某种方式将第一个可执行文件与第一个源、第二个可执行文件与第二个源或类似的东西相匹配?

make 实际上的解释是它为每个目标创建一个规则,all每个规则的先决条件。所以,上面的内容由 make 解释,就像你输入的一样:

bin/prog1.5 : topdir/Gamma/5a5/prog1.5.f03 topdir/Gamma/5a5/prog2.5.f03 ... topdir/Gamma/5a10/prog1.10.f03 topdir/Gamma/5a10/prog2.10.f03 ...
        $(FC) $(FCFLAGS) -o $@ $<
bin/prog2.5 : topdir/Gamma/5a5/prog1.5.f03 topdir/Gamma/5a5/prog2.5.f03 ... topdir/Gamma/5a10/prog1.10.f03 topdir/Gamma/5a10/prog2.10.f03 ...
        $(FC) $(FCFLAGS) -o $@ $<
bin/prog1.10 : topdir/Gamma/5a5/prog1.5.f03 topdir/Gamma/5a5/prog2.5.f03 ... topdir/Gamma/5a10/prog1.10.f03 topdir/Gamma/5a10/prog2.10.f03 ...
        $(FC) $(FCFLAGS) -o $@ $<
bin/prog2.10 : topdir/Gamma/5a5/prog1.5.f03 topdir/Gamma/5a5/prog2.5.f03 ... topdir/Gamma/5a10/prog1.10.f03 topdir/Gamma/5a10/prog2.10.f03 ...
        $(FC) $(FCFLAGS) -o $@ $<

还有所有其余的 bin/* 文件。也许您可以理解您现在遇到的问题:$< 始终扩展到第一个先决条件,而第一个先决条件始终是列表中的第一个文件。显然这是行不通的。

在 make 中,您必须编写一个规则,仅从其先决条件构建一个目标。

您遇到的问题是您正在从许多不同目录将目标构建到一个目录中。这意味着您不能编写一个模式规则,因为没有可以匹配的模式。您可以为每个目录编写一个模式规则,如下所示:

$(BIN)/% : $(FROOT)/5a5/%.f03
        $(FC) $(FCFLAGS) -o $@ $<
$(BIN)/% : $(FROOT)/5a10/%.f03
        $(FC) $(FCFLAGS) -o $@ $<

等等。对于每个目录。

或者你可以use VPATH,正如上面的评论中提到的,写一个单一的模式规则;应该是这样的:

VPATH := $(dir $(SRCF))

$(BIN)/% : %.f03
        $(FC) $(FCFLAGS) -o $@ $<