使用 make 将目标文件存储在单独的目录中

Store object files in separate directory using make

我是 运行 在 windows 上使用 mingw 的 makefile。我看过很多关于这个主题的 SO 链接,但它们似乎都是针对 C 或 C++ 的。我不确定是否适用相同的规则,而且由于我使用的是 windows,语法似乎也有点不同。以下是其他一些参考资料:

How to place object files in separate subdirectory(我认为最有希望)

Using a make file to compile files in separate directories

Flat object file directory structure output with GNU Make

我目前拥有的是(逐字)

VPATH =\
    user \
    static \
    computations \
    solvers\steadyState \
    solvers\transient \
    solvers\transient\momentum \
    solvers\transient\induction

FC      = gfortran
TOP_DIR = .
MOD_DIR = $(TOP_DIR)\mod
OBJ_DIR = $(TOP_DIR)\obj

FCFLAGS = -g
FCFLAGS += -J$(MOD_DIR) -fopenmp -fimplicit-none -Wuninitialized

TARGET = parametricStudy

SRCS_F =\
    user\constants.f90 \
    static\myExceptions.f90 \
    static\myDebug.f90 \
    static\scalarField.f90 \
    static\vectorField.f90 \
    static\myIO.f90 \
    user\simParams.f90 \
    static\solverSettings.f90 \
    static\myTime.f90 \
    computations\myError.f90 \
    static\coordinates.f90 \
    user\griddata.f90 \
    static\myAllocate.f90 \
    static\BCs.f90 \
    user\rundata.f90 \
    computations\myDel.f90 \
    computations\vectorOps.f90 \
    static\myExport.f90 \
    computations\applyBCs.f90 \
    solvers\steadyState\mySOR.f90 \
    solvers\steadyState\myPoisson.f90 \
    solvers\transient\induction\initializeBBCs.f90 \
    solvers\transient\induction\initializeBfield.f90 \
    solvers\transient\induction\initializeSigmaMu.f90 \
    solvers\transient\momentum\initializeUBCs.f90 \
    solvers\transient\momentum\initializeUfield.f90 \
    solvers\transient\inductionSolver.f90 \
    solvers\transient\momentumSolver.f90 \
    solvers\transient\MHDSolver.f90 \
    user\MOONS.f90 \
    parametricStudy.f90

OBJS_F = $(patsubst %.f90,$(OBJ_DIR)\%.o,$(notdir $(SRCS_F)))

all: $(TARGET)

$(TARGET): $(OBJS_F)
    $(FC) -o $@ $(FCFLAGS) $(OBJS_F)

$(OBJ_DIR)\%.o: %.f90
    $(FC) $(FCFLAGS) -c -o $@ $<
clean:
    del $(OBJ_DIR)\*.o $(MOD_DIR)\*.mod parametricStudy.exe
list:;  @echo " "
    @echo " "
    @echo "Source files:"
    @echo $(SRCS_F)
    @echo " "
    @echo "Object files:"
    @echo $(OBJS_F)
    @echo " "
    @echo "Compiler          : $(FC)"
    @echo "Include directory : $(INC_DIR)"
    @echo "Root directory    : $(ROOT_DIR)"
    @echo "Bin directory     : $(BIN_DIR)"
    @echo "Modules directory : $(MOD_DIR)"
    @echo "Modules directory : $(MOD_DIR)"
    @echo "Object directory  : $(OBJ_DIR)"
    @echo " "

使用它,我可以执行以下(再次逐字)

C:\Users\Charlie\Desktop\development\FORTRAN_LIB>gmake
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\const
ants.o user/constants.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\myExc
eptions.o static/myExceptions.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\myDeb
ug.o static/myDebug.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\scala
rField.o static/scalarField.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\vecto
rField.o static/vectorField.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\myIO.
o static/myIO.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\simPa
rams.o user/simParams.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\solve
rSettings.o static/solverSettings.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\myTim
e.o static/myTime.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\myErr
or.o computations/myError.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\coord
inates.o static/coordinates.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\gridd
ata.o user/griddata.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\myAll
ocate.o static/myAllocate.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\BCs.o
 static/BCs.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\runda
ta.o user/rundata.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\myDel
.o computations/myDel.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\vecto
rOps.o computations/vectorOps.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\myExp
ort.o static/myExport.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\apply
BCs.o computations/applyBCs.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\mySOR
.o solvers\steadyState/mySOR.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\myPoi
sson.o solvers\steadyState/myPoisson.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\initi
alizeBBCs.o solvers\transient\induction/initializeBBCs.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\initi
alizeBfield.o solvers\transient\induction/initializeBfield.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\initi
alizeSigmaMu.o solvers\transient\induction/initializeSigmaMu.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\initi
alizeUBCs.o solvers\transient\momentum/initializeUBCs.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\initi
alizeUfield.o solvers\transient\momentum/initializeUfield.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\induc
tionSolver.o solvers\transient\induction/inductionSolver.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\momen
tumSolver.o solvers\transient\momentum/momentumSolver.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\MHDSo
lver.o solvers\transient/MHDSolver.f90
gfortran -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized -c -o .\obj\MOONS
.o user/MOONS.f90
gmake: *** No rule to make target `.\obj\parametricStudy.o', needed by `paramet
ricStudy'.  Stop.

C:\Users\Charlie\Desktop\development\FORTRAN_LIB>gfortran -g -J.\mod -fopenmp -
c -o .\obj\parametricStudy.o parametricStudy.f90

C:\Users\Charlie\Desktop\development\FORTRAN_LIB>gmake
gfortran -o parametricStudy -g -J.\mod -fopenmp -fimplicit-none -Wuninitialized
 .\obj\constants.o .\obj\myExceptions.o .\obj\myDebug.o .\obj\scalarField.o
.\obj\vectorField.o .\obj\myIO.o .\obj\simParams.o .\obj\solverSettings.o .\
\obj\myTime.o .\obj\myError.o .\obj\coordinates.o .\obj\griddata.o .\obj\myA
llocate.o .\obj\BCs.o .\obj\rundata.o .\obj\myDel.o .\obj\vectorOps.o .\obj
\myExport.o .\obj\applyBCs.o .\obj\mySOR.o .\obj\myPoisson.o .\obj\initializ
eBBCs.o .\obj\initializeBfield.o .\obj\initializeSigmaMu.o .\obj\initializeUB
Cs.o .\obj\initializeUfield.o .\obj\inductionSolver.o .\obj\momentumSolver.o
.\obj\MHDSolver.o .\obj\MOONS.o .\obj\parametricStudy.o

请注意,在收到错误后,我可以在错误后显式编译 parametricStudy.f90:

 gfortran -g -J.\mod -fopenmp -c -o .\obj\parametricStudy.o parametricStudy.f90

然后输入

 gmake

同样,没有错误。我很纳闷。

这是我的目录的屏幕截图

可能跟TARGET有关?似乎最后一个文件的路径有点错误。非常感谢任何帮助!

您面临的问题是

ROOT_DIR = "C:\Users\Charlie\"
OBJ_DIR = $(ROOT_DIR)\obj

规则

$(OBJ_DIR)/%.o: %.f90

扩展到

"C:\Users\Charlie\"obj/%.o: %.f90

解析为静态模式规则

"C: \Users\Charlie\"obj/%.o: %.f90

也就是说,有目标 "C,目标模式 \Users\Charlie\"obj/%.o 和先决条件模式 %.f90。 make 抱怨 "C 与模式 \Users\Charlie\"obj/%.o.

不匹配

GNU make 中有一些 hacky 代码(至少在 MinGW 中;我认为 Cygwin 的行为不同,因为它希望您使用其 unix-ish 目录结构)来识别绝对 Windows 路径,但它不处理报价。只要你的 OBJ_DIR 不包含空格,使用

ROOT_DIR = C:\Users\Charlie\

应该启动 Windows 路径识别。

但是...在手工制作的 Makefile 中看到绝对路径是很不寻常的。您确定要这样做吗?一种更常见的方法是使用相对路径,以便在将源代码复制到不同目录时不必更改 Makefile。假设 Makefile 在根目录中,那就是

ROOT_DIR = .

或者完全取消 ROOT_DIR 变量并说

MOD_DIR = mod
OBJ_DIR = obj

哦,然后回答下一个即将出现的问题:为了使 make 使用

$(OBJ_DIR)/%.o: %.f90

规则,您需要使 $(TARGET) 具有与 $(OBJ_DIR)/%.o 模式匹配的先决条件。那可能是

OBJS_F = $(patsubst %.f90,$(OBJ_DIR)/%.o,$(notdir $(SRCS_F)))

我认为以下样式的 makefile 可以解决问题。我用样品C测试了它 项目,不是 fortran,但这对 make 问题和解决方案无关紧要。

# VPATH: Tell `make` to look for in `user` for prerequisites it can't find here
VPATH = user
# If e.g. you also want `make` to look for for prerequisites in `../include`, then:
# VPATH = user:../include
FC      = gfortran
TOP_DIR = .
MOD_DIR = $(TOP_DIR)\mod
OBJ_DIR = $(TOP_DIR)\obj

FCFLAGS = -g
FCFLAGS += -J$(MOD_DIR) -fopenmp -fimplicit-none -Wuninitialized

TARGET = parametricStudy

SRCS_F =\
    user\constants.f90 \
    ...
    parametricStudy.f90

OBJS_T1 = $(patsubst %.f90,%.o,$(SRCS_F))
OBJS_T2 = $(notdir $(OBJS_T1))
# The object files are all to be obj\<name>.o
OBJS_F = $(patsubst %.o,$(OBJ_DIR)\%.o,$(OBJS_T2))

all: $(TARGET)

$(TARGET): $(OBJS_F)
    $(FC) -o $@ $(FCFLAGS) $(OBJS_F)

# How to make an obj\*.o from the matching *.f90. `make` considers the VPATH
$(OBJ_DIR)\%.o: %.f90
    $(FC) $(FCFLAGS) -c -o $@ $<
clean:
    del $(OBJ_DIR)\*.o $(MOD_DIR)\*.mod parametricStudy.exe

使用相对路径 TOP_DIR = . 而不是绝对路径 ROOT_DIR 是已经提供的好建议 通过@Wintermute。

要启用所需的模式规则:

$(OBJ_DIR)\%.o: %.f90

要开始,您必须让 make 看到任何先决条件 *.f90 就在这里,按照模式, 不在其他目录中,例如user\constants.f90 那就是 VPATH 实现。

后续开发继续

我还没有看到 makefile 所在目录的列表 只能冒险假设,但我所看到的假设是:

该目录实际上不包含名为 parametricStudy.f90 的文件, 但是一个名为 parametricStudy.F90 的文件,如果将其重命名为 parametricStudy.f90, 然后 makefile 将找到并编译它。

对吗?

这如何解释事实:模式规则:

$(OBJ_DIR)\%.o: %.f90

无法匹配任何 parametricStudy.f90,因此没有这样的文件。然而你说:

gfortran -g -J.\mod -fopenmp -c -o .\obj\parametricStudy.o parametricStudy.f90

编译成功

您正在 Windows 上构建,因此工具链订阅了 Windows' 文件处理协议。 文件名区分大小写:parametricStudy.f90 将标识 parametricStudy.F90,如果存在, .F90 将被 gfortran(在 Windows 或其他任何地方)解释为表示 Fortran 90 源文件。至此命令行编译成功。

但是模式规则与文件处理协议无关。这只是一个模式 匹配规则,不变量 .f90 不匹配 .F90.