【问题标题】:Makefile with Fortran - src and bin directories带有 Fortran 的 Makefile - src 和 bin 目录
【发布时间】:2013-05-17 00:16:36
【问题描述】:

我在理解如何设计我的 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 - 但是由编译器生成的模块文件(.mod)被放入项目根目录而不是bin 目录。我意识到我可以指定一个规则将它们放在那里,但是that messes with the build order, and will sometimes break the build

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

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

【问题讨论】:

    标签: makefile fortran


    【解决方案1】:

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

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

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

    【讨论】:

    • -J 选项似乎正是我想要的!所有文件最终都在我想要的位置 =) 但是,构建失败,因为gfortran 不知道在bin/ 中查找.mods。我如何指定它们在那里?
    • 另外,关于创建MODDIR - 如果这个项目更大,我会这样做,但它只有大约 15 个源文件(并且不会增长太多),所以一个目录对我来说很好。
    • 这很奇怪 - 第一个文件构建正常;它没有依赖关系。第二个取决于第一个,但可以构建。第三个依赖于第一个和第二个,并且失败,因为它找不到在第二个模块中定义的几个变量 - 但是文件在那里,并且定义了变量(在源文件中正确指定,在 mod 文件中提到,虽然我不知道mod格式所以我不能说那里是否有问题......)。 Here's the mod file - 找不到的符号是 rnknproc
    • 用 -J 指定的目录也被添加到搜索 .mod 文件的目录列表中。也许你有一个旧的 mod 文件被优先从不同的位置拾取,而不是“真正的”mod 文件。
    • 不,不是这样 - 即使我在开始之前删除了所有构建输出,问题仍然存在。实际上,确切的错误消息是“错误:在模块'mpi'中找不到在(1)处引用的符号'rnk'”,所以我猜是找到了模块,但没有找到模块中的变量。
    【解决方案2】:

    从 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 选项以确保找到源文件(因为在处理 makefile 的这一部分时,make 将位于目录 BUILDDIR 中)。

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

    【讨论】:

      猜你喜欢
      • 2011-10-23
      • 2015-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-16
      • 1970-01-01
      相关资源
      最近更新 更多