我有一个C ++代码,可以使用MPI支持编译,具体取决于a 某些预处理器标志;缺少相应的旗帜,来源 编译成非并行版本。
我想设置Makefile.am,以便它编译两者
MPI-parallel 和顺序版本,如果有选项的话
./configure
被给予。
这是一个问题:MPI有自己的C ++编译器包装,并坚持认为 使用它而不是标准编译和链接源 C ++编译器。如果我自己编写Makefile,我必须这样做 做这样的事情:
myprog.seq: myprog.cxx
$(CXX) ... myprog.cxx
myprog.mpi: myprog.cxx
$(MPICXX) -DWITH_MPI ... myprog.cxx
有没有办法告诉automake它必须使用$(MPICXX) $(CXX)编译MPI启用的程序版本?
答案 0 :(得分:7)
我遇到了同样的问题,我发现没有什么好办法让autotools 有条件地使用MPI编译器来处理特定目标。 Autotools善于根据您的来源编写的语言(CC
,CXX
,FC
,F77
等)确定要使用的编译器,但它真的不善于确定是否为特定目标使用MPI编译器。您可以设置MPICC,MPICXX等,但如果您以这种方式使用编译器,则基本上必须重写目标的所有Makefile规则(如上所述)。如果你这样做,编写一个automake文件有什么意义呢?
其他人建议像外部库一样使用MPI,这是我提倡的方法,但你不应该手工完成,因为不同的MPI安装有不同的标志集,它们传递给编译器,可能取决于你正在编译的语言。
好消息是我所知道的所有目前正在运行的MPI编译器都支持内省参数,例如-show
,-show-compile
或-show-link
。您可以从脚本中自动提取参数。
所以,我要做的就是创建一个m4
脚本,从MPI编译器中提取定义,包含,库路径,库和链接器标志,然后将它们分配给可以使用的变量在Makefile.am
中。这是脚本:
这使得MPI的运作方式与automake期望的方式相同。顺便说一句,这是CMake在FindMPI
模块中使用的方法,我发现它在那里工作得很好。它使构建更加方便,因为你可以为你的目标做这样的事情:
bin_PROGRAMS = mpi_exe seq_exe
# This is all you need for a sequential program
seq_exe_SOURCES = seq_exe.C
# For an MPI program you need special LDFLAGS and INCLUDES
mpi_exe_SOURCES = mpi_exe.C
mpi_exe_LDFLAGS = $(MPI_CXXLDFLAGS)
INCLUDES = $(MPI_CXXFLAGS)
其他语言也有类似的标志,因为像我说的那样,特定的标志和库可能会有所不同,具体取决于您使用的语言的MPI编译器。
lx_find_mpi.m4
还设置了一些shell变量,以便您可以在configure.ac
文件中测试是否找到了MPI。例如,如果您正在寻找MPI C ++支持,您可以测试$have_CXX_mpi
以查看宏是否找到它。
我已经使用mvapich和OpenMPI以及MPICH2计算机上的自定义BlueGene实施测试了此宏(尽管它没有解决所有问题编译你会在那里看到的问题)。如果有什么不起作用,请告诉我。我想让宏保持尽可能健壮。
答案 1 :(得分:5)
很抱歉,使用MPI非常困难。我一直在努力寻找一个好的解决方案。我有一个源树,其中有一个库,然后是使用该库的子文件夹中的许多程序。有些文件夹是mpi程序,但当我尝试用Makefile.am
中的MPI编译器替换CXX时。
if USE_MPI
MPIDIR = $(MPICOMPILE)
MPILIB = $(MPILINK)
CXX=@MPICXX@
F77=@MPIF77@
MPILIBS=$(MPILINK)
endif
我得到了
CXX was already defined in condition TRUE, which includes condition USE_MPI ...
configure.ac:12: ... `CXX' previously defined here
我没有指定编译器的规则,所以也许有办法做到这一点。
SUBDIRS = .
bin_PROGRAMS = check.cmr
check_ccmr_SOURCES = check_gen.cpp
check_ccmr_CXXFLAGS = -I$(INCLUDEDIR) $(MPIDIR)
check_ccmr_LDADD = -L$(LIBDIR)
check_ccmr_LDFLAGS = $(MPILIB)
答案 2 :(得分:2)
如果您已将subdir-objects
选项停用为automake
,则此类内容可能会有效:
configure.ac:
AC_ARG_ENABLE([seq], ...)
AC_ARG_ENABLE([mpi], ...)
AM_CONDITIONAL([ENABLE_SEQ], [test $enable_seq = yes])
AM_CONDITIONAL([ENABLE_MPI], [test $enable_mpi = yes])
AC_CONFIG_FILES([Makefile seq/Makefile mpi/Makefile])
Makefile.am:
SUBDIRS =
if ENABLE_SEQ
SUBDIRS += seq
endif
if ENABLE_MPI
SUBDIRS += mpi
endif
sources.am:
ALL_SOURCES = src/foo.c src/bar.cc src/baz.cpp
SEQ / Makefile.am:
include $(top_srcdir)/sources.am
bin_PROGRAMS = seq
seq_SOURCES = $(ALL_SOURCES)
MPI / Makefile.am:
include $(top_srcdir)/sources.am
CXX = $(MPICXX)
AM_CPPFLAGS = -DWITH_MPI
bin_PROGRAMS = mpi
mpi_SOURCES = $(ALL_SOURCES)
唯一阻止你在同一目录中执行这两项工作的是覆盖$(CXX)
。例如,你可以设置mpi_CPPFLAGS
和automake
可以优雅地处理它,但编译器开关使它成为禁止的。
答案 3 :(得分:1)
不使用不同来源的可能解决方法可能是:
myprog.seq: myprog.cxx
$(CXX) ... myprog.cxx
myprog-mpi.cxx: myprog.cxx
@cp myprog.cxx myprog-mpi.cxx
myprog.mpi: myprog-mpi.cxx
$(MPICXX) -DWITH_MPI ... myprog-mpi.cxx
@rm -f myprog-mpi.cxx
用于Automake:
myprog-bin_PROGRAMS = myprog-seq myprog-mpi
myprog_seq_SOURCES = myprog.c
myprog-mpi.c: myprog.c
@cp myprog.c myprog-mpi.c
myprog_mpi_SOURCES = myprog-mpi.c
myprog_mpi_LDFLAGS = $(MPI_CXXLDFLAGS)
INCLUDES = $(MPI_CXXFLAGS)
BUILT_SOURCES = myprog-mpi.c
CLEANFILES = myprog-mpi.c
答案 4 :(得分:1)
这是我提出的用于构建两个静态库的解决方案 - 一个使用MPI(libmylib_mpi.a
),另一个不使用(libmylib.a
)。此方法的优点是不需要重复的源文件,两个变体的单个Makefile.am以及使用子目录的功能。您应该能够根据需要修改它以生成二进制文件而不是库。我正常构建非MPI库,然后对于MPI变体,我将_SOURCES
留空并改为使用_LIBADD
,为目标文件指定.mpi.o
的扩展名。然后,我指定一个规则来使用MPI编译器生成MPI目标文件。
整体文件/目录结构类似于
configure.ac
Makefile.am
src
mylib1.cpp
mylib2.cpp
...
include
mylib.h
...
configure.ac:
AC_INIT()
AC_PROG_RANLIB
AC_LANG(C++)
AC_PROG_CXX
# test for MPI, define MPICXX, etc. variables, and define HAVE_MPI as a condition that will evaluate to true if MPI is available and false otherwise.
AX_MPI([AM_CONDITIONAL([HAVE_MPI], [test "1" = "1"])],[AM_CONDITIONAL([HAVE_MPI], [test "1" = "2"])]) #MPI optional for xio
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
进行条件检查可能比我在此处列出的更有效(我欢迎提出建议)。
Makefile.am:
AUTOMAKE_OPTIONS = subdir-objects
lib_LIBRARIES = libmylib.a
libmylib_a_SOURCES = src/mylib_1.cpp src/mylib_2.cpp ...
#conditionally generate libmylib_mpi.a if MPI is available
if HAVE_MPI
lib_LIBRARIES += libmylib_mpi.a
libmylib_mpi_a_SOURCES = #no sources listed here
#use LIBADD to specify objects to add - use the basic filename with a .mpi.o extension
libmylib_mpi_a_LIBADD = src/mylib_1.mpi.o src/mylib_2.mpi.o ...
endif
AM_CPPFLAGS = -I${srcdir}/include
include_HEADERS = include/mylib.h
# define a rule to compile the .mpi.o objects from the .cpp files with the same name
src/%.mpi.o: ${srcdir}/src/%.cpp ${srcdir}/include/mylib.h
$(MPICXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -DWITH_MPI=1 -c $(patsubst %.mpi.o,$(srcdir)/%.cpp,$@) -o $@
#define a rule to clean the .mpi.o files
clean-local:
-rm -f src/*.mpi.o
答案 5 :(得分:0)
MPI安装(通常)附带编译器包装器,但不要求您使用它们 - MPI 不坚持使用它们。如果你想按自己的方式去编写自己的makefile,以确保C ++编译器能够获得正确的库(等)。要弄清楚正确的库(等)是什么,请检查编译器包装器,在我使用的所有系统上,它是一个shell脚本。
乍一看,与英特尔编译器等产品一起提供的编译器包装器有点令人生畏,但要停下来思考发生了什么 - 您只是编译一个使用外部库或两个库的程序。编写makefile以使用MPI库并不比编写makefile以使用任何其他库困难。