makefile - make 重新编译未更改的文件
问题描述
make 重新编译源代码文件,即使它们没有改变。要重现此行为,我需要执行以下操作:
- make clean,这样就只剩下 *.90 个文件了
- 制作
- 保存 *.f90 文件之一,例如“touchbands.f90”
- make:bands.f90 被重新编译,这是正确的,因为它被改变了
- make:bands.f90 再次重新编译,这是不正确的,因为bands.f90 没有被触及
在这个例子中,在 4. 之后,文件bands.f90 将在每次 make 调用时重新编译。如果我另外更改任何其他源代码文件,则该行为也适用于该文件。所以过了一段时间,我最终重新编译了我所有的源代码。我可以通过调用 make clean 来部分解决这个问题,它会重置循环。但这不是永久修复。
您可以在下面看到上述五个步骤的输出:
1: find . ! -name 'Makefile' -a ! -name '*.f90' -type f -exec rm -f {} +
2: [triggered by changes in const.f90]
mpifort -c const.f90
[triggered by changes in system.f90 const.mod]
mpifort -c system.f90
[triggered by changes in io.f90 system.mod const.mod]
mpifort -c io.f90
[triggered by changes in parallel.f90]
mpifort -c parallel.f90
[triggered by changes in bands.f90 system.mod io.mod const.mod parallel.mod]
mpifort -c bands.f90
[triggered by changes in polmob.f90 io.mod system.mod bands.mod parallel.mod]
mpifort -o polmob polmob.f90 io.f90 system.f90 bands.f90 parallel.f90
3: "no output"
4: [triggered by changes in bands.f90]
mpifort -c bands.f90
5: [triggered by changes in bands.f90]
mpifort -c bands.f90
在第 5 步 make 实际上应该说没有要编译的东西。但是据说bands.f90发生了变化,因此需要重新编译。
这是我的Makefile:
polmob: polmob.f90 io.mod system.mod bands.mod parallel.mod
@echo [triggered by changes in $?]
mpifort -o polmob polmob.f90 io.f90 system.f90 bands.f90 parallel.f90
io.mod: io.f90 system.mod const.mod
@echo [triggered by changes in $?]
mpifort -c io.f90
system.mod: system.f90 const.mod
@echo [triggered by changes in $?]
mpifort -c system.f90
bands.mod: bands.f90 system.mod io.mod const.mod parallel.mod
@echo [triggered by changes in $?]
mpifort -c bands.f90
const.mod: const.f90
@echo [triggered by changes in $?]
mpifort -c const.f90
parallel.mod: parallel.f90
@echo [triggered by changes in $?]
mpifort -c parallel.f90
clean:
find . ! -name 'Makefile' -a ! -name '*.f90' -type f -exec rm -f {} +
标志 $? 告诉我,重新编译是由特定 .f90 文件中的更改触发的。所以以某种方式使涉及到旧的变化。有谁知道这种行为的原因可能是?
解决方案
有谁知道这种行为的原因可能是?
我找到了原因:如果我将 Makefile 中的任何地方的“mod”替换为“o”,问题就会消失。但是,我不太明白为什么。
唯一合理的解释是现有.mod
文件的时间戳在重建原始源时没有更新。我认为只有在模块接口不变的情况下才会扩展——即没有添加或删除函数或子例程,或者以影响其外部接口的方式修改,同样没有添加、删除或更改类型的模块变量.
我可以看到,当文件中没有任何实际更改时,避免更新.mod
文件可能被认为是可取的,特别是与存储在外部库中的模块实现结合使用时。然而,据我所知,甚至文件的存在都没有普遍的标准化.mod
,更不用说创建或更新它们的环境了。您的编译器可能会提供一个选项,在编译相应的源代码时强制更新它们;如果是这样,那么将该选项添加到您的编译命令应该可以解决您的问题。
否则,您的编译器肯定会承诺的是,如果您要求它将源文件编译为目标文件,那么在成功时,它将编写一个新的目标文件。此外,虽然我不一定会猜到.mod
文件也是如此,但重要的是要了解后者通常描述模块的接口,而不是它们的实现,因此您的主要规则在语义上是不正确的:
polmob: polmob.f90 io.mod system.mod bands.mod parallel.mod @echo [triggered by changes in $?] mpifort -o polmob polmob.f90 io.f90 system.f90 bands.f90 parallel.f90
如果任何实现发生变化,无论模块接口是否发生变化,您都需要重新构建,并且您的规则没有充分满足该要求。
在这一点上,我还观察到,当您通过该规则进行重建时,您会重建所有源代码,从而使每个源代码的构建规则变得毫无意义。如果这就是您想要的,那么更简单的解决方案是将您的 while makefile 重写为:
SOURCES = polmob.f90 io.f90 system.f90 bands.f90 parallel.f90
polmob: $(SOURCES)
@echo [triggered by changes in $?]
mpifort -o $@ $(SOURCES)
clean:
find . ! -name 'Makefile' -a ! -name '*.f90' -type f -exec rm -f {} +
在那里,目标实际上是从指定的先决条件构建的,构建配方足以从这些先决条件构建目标。但是,如果任何源发生更改,它将全部重建它们(类似于您的原始 makefile 所做的)。
另一种方法是构建单个对象,然后在单独的步骤中将它们链接在一起。您原来的 makefile 似乎倾向于那个方向,但随后它用主要规则将其丢弃。如果您想采用这种方法,那么由于以下事实而变得复杂
- 它确实是各个对象所依赖的模块接口(除了它们自己的源),因此在这些情况下,用其他对象文件表达先决条件是不正确的;
- 和文件都是由同一个进程构建的(即它有多个输出)
.o
;.mod
- 显然,该过程使用不同的逻辑
make
来确定.mod
文件是否过时。
多输出问题可能是最棘手的问题。您可以在 Automake 文档中找到不同严格程度的讨论和替代解决方案(但不特定于 Automake)。
这是一种您可以解决的方法,受 Automake 文档的启发,但考虑到您在 Fortran 实现中发现的特殊性:
# All the object files contributing to the program:
OBJECTS = bands.o const.o io.o parallel.o polmob.o system.o
# Link all the objects together to form the final program
# (.mod files are typically not needed for this step)
polmob: $(OBJECTS)
mpifort -o $@ $(OBJECTS)
# Rules for building the objects
bands.o : bands.f90 const.mod io.mod parallel.mod system.mod
mpifort -c bands.f90 -o $@
const.o : const.f90
mpifort -c const.f90 -o $@
io.o : io.f90 const.mod system.mod
mpifort -c io.f90 -o $@
parallel.o : parallel.f90
mpifort -c parallel.f90 -o $@
polmob.o : polmob.f90 bands.mod const.mod io.mod parallel.mod system.mod
mpifort -c polmob.f90 -o $@
system.o : system.f90 const.mod
mpifort -c system.f90 -o $@
# Rules for ensuring that .mod files are (re)created when needed, and
# that their timestamps do not fall behind those of their corresponding
# sources
bands.mod : bands.o
@if test -f $@; then touch $@; else \
rm -f bands.o; \
$(MAKE) bands.o; \
fi
const.mod : const.o
@if test -f $@; then touch $@; else \
rm -f const.o; \
$(MAKE) const.o; \
fi
io.mod : io.o
@if test -f $@; then touch $@; else \
rm -f io.o; \
$(MAKE) io.o; \
fi
parallel.mod : parallel.o
@if test -f $@; then touch $@; else \
rm -f parallel.o; \
$(MAKE) parallel.o; \
fi
system.mod : system.o
@if test -f $@; then touch $@; else \
rm -f system.o; \
$(MAKE) system.o; \
fi
####
clean:
find . ! -name 'Makefile' -a ! -name '*.f90' -type f -exec rm -f {} +
这展示了正确的依赖关系:
- 主程序(仅)依赖于所有对象。
- 每个对象都依赖于它自己的源和
.mod
它使用的那些模块的文件。
它还解释了这样一个事实,即相同的编译过程会生成一个目标文件和(在适当的情况下)一个相应的文件,并且它会在需要满足.mod
的地方更新时间戳。它可能偶尔会导致文件在不需要时被重建,因为它会阻止编译器避免更新文件时间戳的任何尝试。但这应该是一次性的,而不是每个后续构建。.mod
make
.mod
推荐阅读
- php - 如何在 PHP 数组中进行分层排序?
- c++ - CUDA - 统一内存(至少帕斯卡)
- python - 如何修改字节数组?
- websocket - JHipster 在微服务架构中支持 websockets
- android - 如何在模型类中保存复杂的json数组和jsonobject
- perl - 在 Perl 中替换变量时将文件复制到另一个文件?
- angular - 从类似 ngForOf 的指令中删除 @Inputs()
- html - “break-inside:避免列”在 Firefox 中不起作用
- java - Java 计数领域结果
- angular - Angular 4从订阅返回数据