问题 如何用automake编译同一程序的MPI和非MPI版本?


我有一个C ++代码,可以使用MPI支持编译,具体取决于a 某些预处理器标志;缺少相应的旗帜,消息来源 编译为非并行版本。

我想设置Makefile.am以便编译  该 MPI并行  顺序版本,如果是一个选项 ./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启用的程序版本?


12466
2017-10-19 13:10


起源

您是否关心串行程序是否与“构建两者”情况下的MPI库链接?如果没有,你可以只使用mpicxx - 它不会伤害任何东西。如果你不是在构建mpi版本,你可以为所有东西设置使用g ++。根据经验,这是许多同时具有并行和串行版本的软件包的例子 - hdf5浮现在脑海中。 - Jonathan Dursi
一般来说,不,我以前用这种方式编译。我开发了一些PMPI工具,一般来说你不想将拦截器库链接到MPI,如果用mpicc编译,你必须这样做。 - tgamblin


答案:


我有同样的问题,我发现没有真正好的方法来获得autotools 有条件的 将MPI编译器用于特定目标。 Autotools善于根据您的源所使用的语言确定要使用的编译器(CCCXXFCF77等等,但它确实不善于确定是否为特定目标使用MPI编译器。您可以设置MPICC,MPICXX等,但如果您以这种方式使用编译器,则基本上必须重写目标的所有Makefile规则(如上所述)。如果你这样做,那么写一个automake文件有什么意义呢?

其他人建议像外部库一样使用MPI,这是我提倡的方法,但你不应该手工完成,因为不同的MPI安装有不同的标志集,它们传递给编译器,它们可以依赖于你正在编译的语言。

好消息是,我所知道的所有目前正在运行的MPI编译器都支持内省参数 -show-show-compile 要么 -show-link。您可以从脚本中自动提取参数。

所以,我所做的就是做一个 m4 从MPI编译器中提取定义,包含,库路径,库和链接器标志的脚本,然后将它们分配给您可以在 Makefile.am。这是脚本:

lx_find_mpi.m4

这使得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 实施 蓝色基因 机器(虽然它没有解决你在那里看到的所有交叉编译问题)。如果有什么不起作用,请告诉我。我想让宏保持尽可能健壮。


7
2017-10-19 21:13



您应该将它添加到LDADD而不是LDFLAGS,否则某些链接器将抛出错误。并且脚本实际有一个错误:使用sed,要删除尾随空格,你应该使用sed / + / / g而不是sed / * / / g。 - w00d
你的宏对我来说很好。但是,我必须运行它两次以获得MPICC和MPICXX的值。在第二个调用中,我将宏包装在之间 AC_LANG_PUSH([C++]) 和 AC_LANG_POP([C++])。谢谢SW。 - xyz


我很抱歉,使用MPI非常困难。我一直在努力寻找一个好的解决方案。我有一个源树,其中有一个库,然后是使用该库的子文件夹中的许多程序。一些文件夹是mpi程序,但是当我尝试使用in中的MPI编译器替换CXX时 Makefile.am

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)

5
2017-07-26 19:58





如果你已经禁用了 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 会优雅地处理它,但编译器开关使它成为一个禁忌。


2
2017-10-19 20:52



Downvote没有评论。做得好。我会认为这是一个肮脏的黑客,但你有更好的主意吗? - Jack Kelly
+1来抵消-1没有评论 - Matt Joiner


不使用不同来源的可能解决方法可能是:

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

1
2018-01-14 17:39



聪明的解决方法,谢谢!我认为有一个错字:第二个 myprog..._SOURCES 行应该读 myprog_mpi_SOURCES = myprog-mpi.c 接着 mpyorog_mpi_LDFLAGS = ... - Riccardo Murri
对了谢谢。我修好了代码! :) - Treviño
您还必须将myprog-mpi.c列为自动生成使用 BUILT_SOURCES = myprog-mpi.c。 - user562374


这是我提出的用于构建两个静态库的解决方案 - 一个带有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

1
2017-10-15 20:04





MPI安装(通常)附带编译器包装器,但不要求您使用它们 - MPI会这样做  坚持下去。如果你想按自己的方式去编写自己的makefile,以确保C ++编译器能够获得正确的库(等)。要弄清楚正确的库(等)是什么,请检查编译器包装器,在我使用的所有系统上,它是一个shell脚本。

乍一看,与英特尔编译器等产品一起提供的编译器包装器有点令人生畏,但要停下来思考发生了什么 - 您只是编译一个使用外部库或两个库的程序。编写makefile以使用MPI库并不比编写makefile以使用任何其他库困难。


0
2017-10-19 13:26



感谢您的回答。虽然我不需要使用MPI编译器包装器,但是有很多MPI实现,每个都使用自己的库名等。 - 我宁愿重写一个Makefile节来使用$(MPICXX)而不是维护几行automake / autoconf代码为每个MPI版本提供CPPFLAGS / LDFLAGS / LIBS ... - Riccardo Murri
如果你关心的是,那就不再困难了 一个平台。但是,我在3或4个不同的集群体系结构上运行,虽然它们都共享一个mpicc编译器,但是不同平台的特定标志和库是不同的。除非你内省了包装器,否则你不能这样做。脚本,这是我建议做的。看到我的回答。 - tgamblin