我正在尝试以各种方式编译源代码。例如,假设我想编译一个 Fortran 程序,其源代码为main.f90
、file1.f90
和file2.f90
作为输入。要生成hello.exe
,我只需执行
FC=gfortran
EXE=hello.exe
OBJS=file1.o file2.o
FFLAGS=
$(EXE): $(OBJS)
$(FC) $(FFLAGS) main.f90 $(OBJS) -o $(EXE)
file1.o: file1.f90
$(FC) $(FFLAGS) -c file1.f90
file2.o: file2.f90
$(FC) $(FFLAGS) -c file2.f90
clean:
rm -rf *.o
现在,假设我想制作此版本的各个版本并比较输出。我想制作一个hello_O2.exe
使用-O2
编译标志的版本,一个hello_O3.exe
带有标志的版本,甚至使用不同的编译器进行编译。例如,-O3
我想要一个hello_intel_O2.exe
使用-O2
标志以及使用英特尔编译器的版本。ifort
理想情况下,我希望指定make intelO3
使用ifort
编译器并-O3
标记哪个和将生成hello_intel_O3.exe
可执行文件。如果我指定,它将make all
创建hello_O2.exe
、、和二进制文件,每个文件都具有正确的编译器和优化级别。hello_O3.exe
hello_intel_O2.exe
hello_intel_O3.exe
在这个最小的例子中如何实现这一点?我实际的 Makefile 现在大约有 120 行,并且使用了许多变量,每次我想要构建不同的版本时,我都会更改这些变量。
答案1
当你将 makefile 中设置的任何变量作为参数提供给 make 命令时,你可以简单地覆盖它们。例如:
#!/bin/make
FC=gfortran
EXE=hello$(SUFFIX).exe
OBJS=file1.o file2.o
FFLAGS=
default: $(EXE)
intelO3:
$(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3
all:
$(MAKE) FFLAGS=-O2 SUFFIX=_O2
$(MAKE) FFLAGS=-O3 SUFFIX=_O3
$(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3
... rest of makefile
答案2
我建议,就像您需要
.exe
程序文件的多个不同副本一样,您也需要维护.o
文件的多个副本。如下所示:file1_gfortran.o: file1.f90 gfortran $(FFLAGS) -c file1.f90 -o file1_gfortran.o file2_gfortran.o: file2.f90 gfortran $(FFLAGS) -c file2.f90 -o file2_gfortran.o file1_ifort.o: file1.f90 ifort $(FFLAGS) -c file1.f90 -o file1_ifort.o file2_ifort.o: file2.f90 ifort $(FFLAGS) -c file2.f90 -o file2_ifort.o
否则你需要重新编译每一个
.f90
file3.f90
每次修改其中任何一个时,都会将其归档。(当您有、等时,这将变得很重要file4.f90
……)- 您也许能够消除一些冗长的冗余;例如,使用
-o $<
。另请参阅下面的第 5 段。 FFLAGS
如果除了标志之外还有别的东西,那么你可能需要有两个不同的副本。-On
- 我猜你会想要定义
OBJS=file1_$(FC).o file2_$(FC).o
。
- 您也许能够消除一些冗长的冗余;例如,使用
我建议你不要只列出命令,而要明确标识出你(可能)想要生成的所有目标文件;例如,
hello.exe: $(MAKE) hello_O2.exe: $(MAKE) FFLAGS=-O2 SUFFIX=_O2 hello_O3.exe: $(MAKE) FFLAGS=-O3 SUFFIX=_O3 hello_intel.exe: $(MAKE) FC=ifort SUFFIX=_intel hello_intel_O2.exe: $(MAKE) FC=ifort FFLAGS=-O2 SUFFIX=_intel_O2 hello_intel_O3.exe: $(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3
那么你可以说
intelO3: hello_intel_O3.exe
而不必输入
$(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3
两次命令。那么,显然,你会说:all: hello.exe hello_O2.exe hello_O3.exe hello_intel.exe hello_intel_O2.exe hello_intel_O3.exe
实际上,以上方法可能更适合伪目标:
all: gf gfO2 gfO3 intel intelO2 intelO3 gf: $(MAKE) FC=gfortran gfO2: $(MAKE) FC=gfortran FFLAGS=-O2 SUFFIX=_O2 gfO3: $(MAKE) FC=gfortran FFLAGS=-O3 SUFFIX=_O3 intel: $(MAKE) FC=ifort SUFFIX=_intel intelO2: $(MAKE) FC=ifort FFLAGS=-O2 SUFFIX=_intel_O2 intelO3: $(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3
然后你也许可以将上面的最后六节折叠起来就像是这:
gf gfO2 gfO3 intel intelO2 intelO3: case $< in (gf*) compiler=gfortran; suffix="";; \ (in*) compiler=ifort; suffix="_intel";; esac; \ case $< in (*O2) flags="-O2"; suffix="${suffix}_O2";; \ (*O3) flags="-O3"; suffix="${suffix}_O3";; esac; \ $(MAKE) FC="$compiler" FFLAGS="$flags" SUFFIX="$suffix"
请注意,这是一个多行 shell 命令(行尾以反斜杠结尾)。如果您想在一行中设置 shell 变量并在下一行中使用它,则需要这样做;的默认行为是
make
为每个命令行生成一个单独的 shell 进程,从而丢失本地副作用,如工作目录(cd
)和 shell 变量。回到第一段:你也许可以将其简化为
file1_gfortran.o file1_ifort.o: file1.f90 case $< in (_gf*) compiler=gfortran;; \ (_if*) compiler=ifort;; esac; \ "$compiler" $(FFLAGS) -c file1.f90 -o $< file2_gfortran.o file2_ifort.o: file2.f90 case $< in (_gf*) compiler=gfortran;; \ (_if*) compiler=ifort;; esac; \ "$compiler" $(FFLAGS) -c file2.f90 -o $<
并且我认为您可以将上述内容合并为一个节(以类似的内容开头),但我不记得该怎么做。
X_gfortran.o X_ifort.o: X.f90
警告:我还没有测试过这些。