我有一个包含许多虚假目标的make文件,它们都使用不同的编译标志编译相同的代码。
EXECUTABLE=ecis
#debug build
.PHONY: debug
debug: FLAGS=-g
debug: $(EXECUTABLE)
#No optimization
.PHONY: opt0
opt0: FLAGS=
opt0: $(EXECUTABLE)
#level 1 optimization
.PHONY: opt1
opt1: FLAGS=-O1
opt1: $(EXECUTABLE)
#level 2 optimization
.PHONY: opt2
opt2: FLAGS=-O2
opt2: $(EXECUTABLE)
...
$(EXECUTABLE):$(FORTRAN_OBJECTS) $(CPP_OBJECTS)
$(CPP) $(FLAGS) $(FORTRAN_OBJECTS) $(CPP_OBJECTS) -lgfortran -o $@
...
当我第一次使用一个构建make opt2
选项运行makefile时,它运行得很好。
如果我随后想要使用另一个构建选项运行make,请说make debug
它声称目标是最新的。我理解为什么这样做,make没有意识到标志已经改变,所以就make而言,如果文件没有改变,没有任何改变。
那说,除了调用make cleanall
(删除.o文件和可执行文件)之外,还有一种简单的方法吗?是否有一些方法可以将不同的标志识别为更改编译?还有其他途径让它“做正确的事”吗?
答案 0 :(得分:2)
这是一个应该有效的例子:
.compile_flags: Makefile
[ "`cat $@`" = '$(FLAGS)' ] || echo '$(FLAGS)' > $@
$(FORTRAN_OBJECTS) $(CPP_OBJECTS) $(EXECUTABLE): .compile_flags
另一种方法是将目标文件和可执行文件生成到每个不同基本目标的单独子目录中。然后他们不会重叠。这样做的另一个好处是,您不必为每种类型的构建重新编译世界(仅在您上次执行该构建时)。但是它会占用更多的磁盘空间,并且如果系统的其他部分期望生活在他们现在所处的位置,则可能会导致其他问题。
使用特定于目标的变量无法将事物放入其他目录中。最简单的方法是使用递归make的一个实例,如下所示:
EXECUTABLE=ecis
#debug build
.PHONY: debug
debug: FLAGS=-g
#No optimization
.PHONY: opt0
opt0: FLAGS=
#level 1 optimization
.PHONY: opt1
opt1: FLAGS=-O1
#level 2 optimization
.PHONY: opt2
opt2: FLAGS=-O2
debug opt0 opt1 opt2:
$(MAKE) OUTDIR=obj_$@ FLAGS=$(FLAGS) obj_$@/$(EXECUTABLE)
...
FORTRAN_OBJECTS := $(addprefix $(OUTDIR)/,$(FORTRAN_OBJECTS))
CPP_OBJECTS := $(addprefix $(OUTDIR)/,$(CPP_OBJECTS))
$(OUTDIR)/$(EXECUTABLE):$(FORTRAN_OBJECTS) $(CPP_OBJECTS)
$(CPP) $(FLAGS) $(FORTRAN_OBJECTS) $(CPP_OBJECTS) -lgfortran -o $@
...
而且,您必须为您的目标文件创建模式规则,例如:
$(OUTDIR)/%.o : %.cpp
...
和FORTRAN同上。
答案 1 :(得分:1)
如果您仅限于GNU make,那么您将无法实现目标。正如@MadScientist建议的那样,你可以基本上得到你想要的一些恶作剧。 John Graham-Cumming在他的旧“Ask Mr. Make”栏目中写下了一个很好的解释:Rebuilding when CPPFLAGS Changes。
如果你可以使用其他make实现,你可以查看Electric Make,这是为实现性能和可靠性而设计的GNU make的重新实现。它包括一个名为“分类帐”的功能,可以精确地提供此功能。
免责声明:我是Electric Make的架构师和首席开发人员