我在理解如何设计makefile以按照我想要的方式构建项目时遇到了一些麻烦。具体来说,我无法弄清楚如何将所有源文件保存在src
目录中,同时将所有二进制文件放在bin
目录中,但链接的可执行文件除外,该目录位于项目根目录中。
这是我的makefile:
# Compiler options
FC := mpif90
FFLAGS := -O3 -g -Wall -Warray-bounds -ffixed-line-length-none -fbounds-check
VPATH := src
BINDIR := bin
# Define file extensions
.SUFFIXES:
.SUFFIXES: .f .o .mod
# All modules
OBJS := $(BINDIR)/ratecoeffs.o $(BINDIR)/interpolation.o $(BINDIR)/io.o $(BINDIR)/eedf.o $(BINDIR)/single_particle.o $(BINDIR)/physics.o $(BINDIR)/random.o $(BINDIR)/mpi.o $(BINDIR)/precision.o $(BINDIR)/populations.o
# Build rules
all: runner | $(BINDIR)
$(BINDIR):
mkdir -p $(BINDIR)
$(BINDIR)/%.o: $(VPATH)/%.f | $(BINDIR)
$(FC) $(FFLAGS) -c $^ -o $@
runner: $(OBJS)
clean:
@rm -rf $(BINDIR)
正在运行make
构建所有内容 - 它会在src
中找到所有源文件,并将所有.o
个文件放入bin
- 但模块文件(.mod
)由编译器生成的文件放在项目根目录中,而不是放在bin
目录中。我意识到我可以指定一个规则将它们放在那里,但是that messes with the build order, and will sometimes break the build。
获得此行为的“正确”方式是什么?
是的,我看过autotools和automake,但我之前从未使用过它们,这对于这个项目来说似乎有些过分。因为我找不到关于它们如何工作的任何好的教程(不,我不喜欢the tutorial on gnu.org)我更愿意,如果我可以避免必须学习这个工具只是为了让这项工作...
答案 0 :(得分:1)
假设您的基础Fortran编译器是gfortran,请使用-J命令行选项。
$(FC) $(FFLAGS) -c $^ -o $@ -J$(BINDIR)
着眼未来,您可能最好创建一个MODDIR或类似的变量,而不是使用BINDIR。对象代码(* .o)和mod文件在以后的编译和链接步骤中具有不同的角色 - 在较大的项目中,它们通常是分开的。
答案 1 :(得分:0)
make系统可能会更改为obj目录并从那里进行编译。通过VPATH
选项,您可以让make自动查找源文件。您可以从右侧目录中以递归方式轻松调用makefile。下面是一个简单的例子,可以直接适应你的情况。请注意,它只适用于GNU make。
ifeq (1,$(RECURSED))
VPATH = $(SRCDIR)
########################################################################
# Project specific makefile
########################################################################
FC = gfortran
FCOPTS =
LN = $(FC)
LNOPTS =
OBJS = accuracy.o eqsolver.o io.o linsolve.o
linsolve: $(OBJS)
$(LN) $(LNOPTS) -o $@ $^
%.o: %.f90
$(FC) $(FCOPTS) -c $<
.PHONY: clean realclean
clean:
rm -f *.mod *.o
realclean: clean
rm -f linsolve
accuracy.o:
eqsolver.o: accuracy.o
io.o: accuracy.o
linsolve.o: accuracy.o eqsolver.o io.o
else
########################################################################
# Recusive invokation
########################################################################
BUILDDIR = _build
LOCALGOALS = $(BUILDDIR) distclean
RECURSIVEGOALS = $(filter-out $(LOCALGOALS), $(MAKECMDGOALS))
.PHONY: all $(RECURSIVE_GOALS) distclean
all $(RECURSIVEGOALS): $(BUILDDIR)
+$(MAKE) -C $(BUILDDIR) -f $(CURDIR)/GNUmakefile SRCDIR=$(CURDIR) \
RECURSED=1 $(RECURSIVEGOALS)
$(BUILDDIR):
mkdir $(BUILDDIR)
distclean:
rm -rf $(BUILDDIR)
endif
原则很简单:
在第一部分中,您将编写正常的makefile,就好像您将在源目录中创建目标文件一样。但是,另外添加VPATH
选项以确保找到源文件(当make文件的这一部分被处理时,make将位于目录BUILDDIR
中)。
在第二部分(首先执行,当时尚未设置变量RECURSED
)时,您将切换到BUILDIR
目录并从那里调用您的makefile。你传递了一些辅助变量(例如当前目录)和所有make目标,除了那些必须从外部BUILDDIR
执行的目标(例如,distclean和创建BUILDDIR
本身的目标)。您在第二部分中指定的目标规则。