Try linking the code created by C and Fortran compilers together.
If it fails, try removing LTO flags in case we are combining two
incompatible compilers (Clang and GNU Fortran).

I don't love this implementation but I think it's the simplest solution
that is quite reliable at the same time.  If compilers don't seem to
work, it does not change anything.  In the worst case, it will disable
LTO unnecessarily.

Closes: https://bugs.gentoo.org/965176
Signed-off-by: Michał Górny <[email protected]>
---
 eclass/fortran-2.eclass | 90 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 82 insertions(+), 8 deletions(-)

diff --git a/eclass/fortran-2.eclass b/eclass/fortran-2.eclass
index dcf7ee979d8af..5b64244bb70f4 100644
--- a/eclass/fortran-2.eclass
+++ b/eclass/fortran-2.eclass
@@ -34,7 +34,7 @@ case ${EAPI} in
        *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
 esac
 
-inherit toolchain-funcs
+inherit flag-o-matic toolchain-funcs
 
 # @ECLASS_VARIABLE: FORTRAN_NEED_OPENMP
 # @DESCRIPTION:
@@ -187,6 +187,66 @@ _fortran-has-openmp() {
        return ${ret}
 }
 
+# @FUNCTION: _fortran-test-lto
+# @INTERNAL
+# @DESCRIPTION:
+# Test if C and Fortran files can be linked together.  If it fails, remove
+# LTO flags.  This may be necessary if users are combining Clang
+# with GNU Fortran, and enabling LTO, since this will result in two different
+# kinds of bytecode being linked with a linker that's set up for only one
+# of them.
+_fortran-test-lto() {
+       debug-print-function ${FUNCNAME} "$@"
+
+       local compiler=${1}
+       local flags=${2}
+       local fcode=${T}/test-fc.f
+       local ccode=${T}/test-fc.c
+       local fail=
+
+       einfo "Testing linking C and Fortran code ..."
+
+       cat <<- EOF > "${fcode}" || die
+                      subroutine fsub()
+                      end
+       EOF
+       cat <<- EOF > "${ccode}" || die
+               void fsub_();
+
+               int main() {
+                       fsub_();
+                       return 0;
+               }
+       EOF
+
+       # Prepare test objects.  If either failed, do nothing.
+
+       set -- ${compiler} ${flags} -c "${fcode}" -o "${fcode}.o"
+       echo "${@}" >&2
+       "${@}" || return
+
+       set -- $(tc-getCC) ${CFLAGS} -c "${ccode}" -o "${ccode}.o"
+       echo "${@}" >&2
+       "${@}" || return
+
+       # Try cross-linking.
+
+       set -- ${compiler} ${flags} ${LDFLAGS} -o "${fcode}.exe" "${fcode}.o" 
"${ccode}.o"
+       echo "${@}" >&2
+       "${@}" || fail=1
+
+       set -- $(tc-getCC) ${CFLAGS} ${LDFLAGS} -o "${fcode}.exe" "${fcode}.o" 
"${ccode}.o"
+       echo "${@}" >&2
+       "${@}" || fail=1
+
+       if [[ ! ${fail} ]]; then
+               einfo "Looks like we can link C and Fortran code together"
+       else
+               einfo "Cross-linking C and Fortran failed, force-disabling LTO"
+               filter-lto
+       fi
+}
+
 # @FUNCTION: _fortran_die_msg
 # @INTERNAL
 # @DESCRIPTION:
@@ -211,19 +271,31 @@ _fortran_die_msg() {
 _fortran_test_function() {
        debug-print-function ${FUNCNAME} "$@"
 
-       local dialect
+       local compiler dialect flags
 
        : "${F77:=$(tc-getFC)}"
 
        : "${FORTRAN_STANDARD:=77}"
        for dialect in ${FORTRAN_STANDARD}; do
                case ${dialect} in
-                       77) _fortran_compile_test "$(tc-getF77)" || \
-                               _fortran_die_msg ;;
-                       90|95) _fortran_compile_test "$(tc-getFC)" 90 || \
-                               _fortran_die_msg ;;
-                       2003) _fortran_compile_test "$(tc-getFC)" 03 || \
-                               _fortran_die_msg ;;
+                       77)
+                               compiler=$(tc-getF77)
+                               flags=${FFLAGS}
+                               _fortran_compile_test "${compiler}" ||
+                                       _fortran_die_msg 
+                               ;;
+                       90|95)
+                               compiler=$(tc-getFC)
+                               flags=${FCFLAGS}
+                               _fortran_compile_test "${compiler}" 90 ||
+                                       _fortran_die_msg
+                               ;;
+                       2003)
+                               compiler=$(tc-getFC)
+                               flags=${FCFLAGS}
+                               _fortran_compile_test "${compiler}" 03 ||
+                                       _fortran_die_msg
+                               ;;
                        2008) die "Future" ;;
                        *) die "${dialect} is not a Fortran dialect." ;;
                esac
@@ -241,6 +313,8 @@ _fortran_test_function() {
                        die "Please install current gcc with USE=openmp or set 
the FC variable to a compiler that supports OpenMP"
                fi
        fi
+
+       _fortran-test-lto "${compiler}" "${flags}"
 }
 
 # @FUNCTION: _fortran-2_pkg_setup

Reply via email to