2

我在理解如何设计我的 makefile 以按照我想要的方式构建我的项目时遇到了一些麻烦。具体来说,我不知道如何将所有源文件保存在一个src目录中,同时将所有二进制文件放在一个bin目录中,除了链接的可执行文件,它位于项目根目录中。

这是我的生成文件:

# 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的模块文件 ( ) 放在项目根目录中而不是目录中。我意识到我可以指定一个规则将它们放在那里,但这会与构建顺序混淆,并且有时会破坏 build.modbin

获得这种行为的“正确”方法是什么?

是的,我看过 autotools 和 automake,但我以前从未使用过它们,而且对于这个项目来说,它们似乎有点矫枉过正。因为我找不到任何关于它们如何工作的好的教程(不,我不喜欢gnu.org 上的教程),我宁愿避免为了完成这项工作而不得不学习这个工具......

4

2 回答 2

1

假设您的底层 Fortran 编译器是 gfortran,请使用 -J 命令行选项。

$(FC) $(FFLAGS) -c $^ -o $@ -J$(BINDIR)

着眼于未来,您最好创建一个 MODDIR 或类似变量,而不是 BINDIR。目标代码 (*.o) 和 mod 文件在以后的编译和链接步骤中扮演不同的角色 - 在较大的项目中,它们通常是分开的。

于 2013-05-17T02:47:57.540 回答
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确保找到源文件的选项(因为在BUILDDIR处理 makefile 的这一部分时,make 将在目录中)。

  • 在第二部分(首先执行,当变量RECURSED尚未设置时),您切换到BUILDIR目录并从那里调用您的 makefile。您传递了一些辅助变量(例如当前目录)和所有 make 目标,除了那些必须从外部执行的目标BUILDDIR(例如 distclean 和创建BUILDDIR自身的目标)。您在第二部分中指定的那些目标的规则。

于 2013-05-17T06:37:41.220 回答