From a083686be67e87fcfd55cc9154e90ac4b4df032b Mon Sep 17 00:00:00 2001 From: Jeff Squyres Date: Tue, 2 Jul 2024 08:49:04 -0400 Subject: [PATCH] ompi_setup_fc.m4: use -Wl,-ld_classic if supported Per https://github.com/open-mpi/ompi/issues/12427, on MacOS, add -Wl,-ld_classic to the Fortran wrapper compiler if that flag is needed. Specifically, Open MPI has used -Wl,-commons,use_dylibs for decades to support common symbols (e.g., MPI_BOTTOM) in the Fortran bindings. There is a window of Xcode versions where this switch was effectively disabled; it effectively required the additional -Wl,-ld_classic switch to force the use of the "old" Apple linker (that still supported -Wl,-commons,use_dylibs). Update the configury to test whether we need -Wl,-ld_classic or not. Signed-off-by: Jeff Squyres --- config/ompi_setup_fc.m4 | 171 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 157 insertions(+), 14 deletions(-) diff --git a/config/ompi_setup_fc.m4 b/config/ompi_setup_fc.m4 index 0632bce7a5d..4ead472ce76 100644 --- a/config/ompi_setup_fc.m4 +++ b/config/ompi_setup_fc.m4 @@ -50,9 +50,163 @@ AC_DEFUN_ONCE([_OMPI_SETUP_FC_COMPILER],[ ############################################################################# +dnl On macOS with Xcode, test whether -Wl,-commons,use_dylibs works +dnl by itself or whether it also needs -Wl,-ld_classic. +dnl +dnl * It seems to always work (by itself) when building shared +dnl libraries. +dnl * It seems to fail (by itself) when building shared libraries for +dnl certain versions of Xcode. In these failure scenarios, we add +dnl -Wl,-ld_classic to make it work. +dnl +dnl Note: the -ld_classic flag works with both static and shared +dnl libraries, so it is safe to always do this test -- even if +dnl we are only building static libraries. +dnl +dnl The history is that for a long time (decades), +dnl -Wl,-commons,use_dylibs worked by itself. But then Apple decided +dnl to remove support for it, and cause link-time errors with MPI +dnl Fortran sentinel values (e.g., MPI_BOTTOM) because they are +dnl implemented with Fortran common symbols. Later, Xcode v16 +dnl (re)added support for -Wl,commons,use_dylibs again. Hence, there +dnl is a window of a few versions of Xcode that need the additional +dnl -Wl,ld_classic flag to link/work properly. +dnl +dnl We have to use a slightly complex test code that will actually +dnl fail if the version of Xcode being used requires -Wl,-ld_classic +dnl with -Wl,-commons,use_dylibs. +dnl +dnl 1. Build a shared library (with C source code) with a common +dnl symbol. +dnl 2. Compile a Fortran program that calls a function in the shared +dnl library, and link it against the shared library. +AC_DEFUN([_OMPI_SETUP_FC_XCODE_COMMONS_LDFLAGS],[ + OPAL_VAR_SCOPE_PUSH([xcode_flags]) + + # This variable is used by the invoking macro to display the + # results via AC RESULT (just to keep the symmetry of + # MSG_CHECKING / RESULT in the same upper-level macro). + OMPI_FORTRAN_WRAPPER_FLAGS= + + xcode_flags="-Wl,-commons,use_dylibs" + _OMPI_SETUP_FC_XCODE_COMMONS_LDFLAGS_BACKEND( + [$xcode_flags], + [OMPI_FORTRAN_WRAPPER_FLAGS=$xcode_flags], []) + AS_IF([test -z "$OMPI_FORTRAN_WRAPPER_FLAGS"], + [xcode_flags="-Wl,-commons,use_dylibs -Wl,-ld_classic" + _OMPI_SETUP_FC_XCODE_COMMONS_LDFLAGS_BACKEND( + [$xcode_flags], + [OMPI_FORTRAN_WRAPPER_FLAGS=$xcode_flags], [])]) + AS_IF([test -z "$OMPI_FORTRAN_WRAPPER_FLAGS"], + [OMPI_FORTRAN_WRAPPER_FLAGS="none"]) + + OPAL_VAR_SCOPE_POP +]) + +dnl Companion to _OMPI SETUP_FC_XCODE_COMMONS_LDFLAGS; +dnl see that macro for an explanation of this macro. +dnl +dnl $1: LDFLAGS to test +dnl $2: action to perform upon success +dnl $3: action to perform upon failure +AC_DEFUN([_OMPI_SETUP_FC_XCODE_COMMONS_LDFLAGS_BACKEND],[ + OPAL_VAR_SCOPE_PUSH([xcode_happy xcode_dir LDFLAGS_save_xcode LIBS_save_xcode]) + + xcode_dir=conftest.$$ + rm -rf $xcode_dir + mkdir -p $xcode_dir + cd $xcode_dir + + LIBS_save_xcode=$LIBS + LDFLAGS_save_xcode=$LDFLAGS + LDFLAGS="$LDFLAGS -L. $1" + + # Note: we use COMPILE_IFELSE and LANG_SOURCE below, which assume + # that confdefs.h exists. This is being invoked extremely early + # in the configure sequence, so we haven't AC DEFINE'ed anything + # yet, and therefore confdefs.h won't be automatically created + # yet. So we'll make an empty confdefs.h to avoid some error + # messages (it'll be removed with the whole tempdir, later). + touch confdefs.h + + # Step 1: make a C library with some public symbols + xcode_happy=0 + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +/* Must end the symbol in _ (remember: we are targeting the MacOS + compilation environment, otherwise the Fortran linker will not find + it, and will just create a new Fortran symbol for it */ +int ompi_mpi_bottom_ = 42; + +void ompi_init_f(int *bogus); + +/* Empty / useless function that ensures that this compilation + unit will not be optimized out */ +void ompi_init_f(int *bogus) +{ + /* Have a non-optimizable function body + (but is otherwise worthless) */ + *bogus = ompi_mpi_bottom_; +} +]])], + [ # If the above compiled successfully, Then use + # conftest.OBJEXT to make the library. Note that + # conftest.OBJEXT will automatically be deleted upon exit of + # COMPILE_IFELSE. + OPAL_LOG_COMMAND([$CC -dynamiclib -Wl,-undefined -Wl,dynamic_lookup $LDFLAGS conftest.$OBJEXT -o libconftest.dylib], + [xcode_happy=1])]) + AC_LANG_POP + + # Now compile and link a Fortran program against this shared + # library + AC_LANG_PUSH([Fortran]) + AS_IF([test $xcode_happy -eq 1], + [LIBS="$LIBS -lconftest" + AC_LINK_IFELSE([AC_LANG_SOURCE([ +program test + integer :: mpi_bottom + common/ompi_mpi_bottom/mpi_bottom + + interface + subroutine ompi_init(bogus) BIND(C, name="ompi_init_f") + implicit none + integer bogus + end subroutine ompi_init + end interface + + integer bogus + call ompi_init(bogus) +end program +])], + + [], + [xcode_happy=0])]) + AC_LANG_POP + + # Exit the temp dir + cd .. + rm -rf $xcode_dir + + # LIBS was set specifically for the artificial conditions of this + # test, so reset it + LIBS=$LIBS_save_xcode + + AS_IF([test $xcode_happy -eq 1], + [$2], + [ # If we failed the test, reset LDFLAGS (if we passed the + # test, leave LDFLAGS modified with the new flags so that + # we use them to build Open MPI). + LDFLAGS=$LDFLAGS_xcode_save + $3]) + + OPAL_VAR_SCOPE_POP +]) + +############################################################################# + # General Fortran compiler setup AC_DEFUN([OMPI_SETUP_FC],[ - OPAL_VAR_SCOPE_PUSH([ompi_fc_happy LDFLAGS_save fc_version]) + OPAL_VAR_SCOPE_PUSH([ompi_fc_happy LDFLAGS_save fc_version OMPI_FORTRAN_WRAPPER_FLAGS]) # Force the intro banner to be displayed first AC_REQUIRE([_OMPI_SETUP_FC_BANNER]) @@ -226,23 +380,12 @@ I = 3]])], ]) ]) - # Per #1982, on OS X, we may need some esoteric linker flags in the + # Per Trac #1982, on OS X, we may need some esoteric linker flags in the # Fortran wrapper compiler. AC_MSG_CHECKING([to see if mpifort compiler needs additional linker flags]) case "$host" in *apple-darwin*) - # Test whether -Wl,-commons,use_dylibs works; if it does, use it. - LDFLAGS_save=$LDFLAGS - LDFLAGS="$LDFLAGS -Wl,-commons,use_dylibs" - AC_LANG_PUSH([Fortran]) - AC_LINK_IFELSE([AC_LANG_SOURCE([[program test - integer :: i -end program]])], - [OMPI_FORTRAN_WRAPPER_FLAGS="-Wl,-commons,use_dylibs" - OPAL_WRAPPER_FLAGS_ADD([FCFLAGS], [$OMPI_FORTRAN_WRAPPER_FLAGS])], - [OMPI_FORTRAN_WRAPPER_FLAGS=none]) - AC_LANG_POP([Fortran]) - LDFLAGS=$LDFLAGS_save + _OMPI_SETUP_FC_XCODE_COMMONS_LDFLAGS AC_MSG_RESULT([$OMPI_FORTRAN_WRAPPER_FLAGS]) ;; *)