#!/bin/bash
# Copyright © 2025 Collabora Ltd.
# SPDX-License-Identifier: MIT

set -e
set -u

test_num=0

ok () {
    test_num=$(( test_num + 1 ))
    echo "ok $test_num - $*"
}

done_testing () {
    echo "# Test finished"
    echo "1..$test_num"
}

verbosely () {
    printf '# %s\n' "$(printf '%q ' "$@")"
    "$@"
}

tmpdir="$(mktemp -d)"
cleanup () {
    rm -fr "$tmpdir"
}
trap cleanup 0 INT QUIT ABRT PIPE TERM

MAJOR_VERSION=14
LIBSTDCXX_VERSION=20250212
DEB_BUILD_GNU_TYPE="$(dpkg-architecture -qDEB_BUILD_GNU_TYPE)"
i386_gnu_type="$(dpkg-architecture -ai386 -qDEB_HOST_GNU_TYPE 2>/dev/null)"
arch="$(dpkg --print-architecture)"

case "$arch" in
    (i386)
        echo "1..0 # SKIP - not supported on i386"
        exit 0
        ;;
esac

cat > "$tmpdir/t.c" <<EOF
/* provided by the compiler */
#include <stddef.h>

/* provided by glibc */
#include <stdio.h>

int main(void)
{
#if defined(__clang__) || !defined(__GNUC__)
#error not gcc
#endif

#if __GNUC__ < $MAJOR_VERSION
#error gcc too old
#endif

  puts ("# OK");
  return 0;
}
EOF

cat > "$tmpdir/t.cxx" <<EOF
// Provided by libstdc++ in a non-architecture-specific location,
// and includes bits/c++config.h which is architecture-specific,
// confirming that both kinds of header work
#include <cstdio>

int main(void)
{
#if defined(__clang__) || !defined(__GNUC__)
#error not gcc
#endif

#ifndef __cplusplus
#error not C++
#endif

#if __GNUC__ < $MAJOR_VERSION
#error gcc too old
#endif

#if __GLIBCXX__ < $LIBSTDCXX_VERSION
#error libstdc++ too old
#endif

  ::puts ("# OK");
  return 0;
}
EOF

for prefix in "" "${DEB_BUILD_GNU_TYPE}-"; do
    CC="${prefix}gcc-$MAJOR_VERSION"
    CXX="${prefix}g++-$MAJOR_VERSION"

    verbosely "$CC" -o"$tmpdir/${prefix}c" "$tmpdir/t.c"
    ok "compiled C"
    verbosely file "$tmpdir/${prefix}c"
    verbosely "$tmpdir/${prefix}c"
    ok "ran C"

    verbosely "$CXX" -o"$tmpdir/${prefix}cxx" "$tmpdir/t.cxx"
    ok "compiled C++"
    verbosely file "$tmpdir/${prefix}cxx"
    verbosely "$tmpdir/${prefix}cxx"
    ok "ran C++"

    CXX="${prefix}c++-$MAJOR_VERSION"
    verbosely "$CXX" -o"$tmpdir/${prefix}cxx" "$tmpdir/t.cxx"
    ok "compiled C++"
    verbosely file "$tmpdir/${prefix}cxx"
    verbosely "$tmpdir/${prefix}cxx"
    ok "ran C++"
done

if [ "$arch" = amd64 ]; then
    CC="gcc-$MAJOR_VERSION"
    CXX="g++-$MAJOR_VERSION"

    verbosely "$CC" -m32 -o"$tmpdir/c-m32" "$tmpdir/t.c"
    ok "compiled C for 32-bit via -m32"
    verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/c-m32"
    ok "it is really 32-bit"
    verbosely "$tmpdir/c-m32"
    ok "ran C for 32-bit"

    verbosely "$CXX" -m32 -o"$tmpdir/cxx-m32" "$tmpdir/t.cxx"
    ok "compiled C++ for 32-bit via -m32"
    verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/cxx-m32"
    ok "it is really 32-bit"
    verbosely "$tmpdir/cxx-m32"
    ok "ran C++ for 32-bit"

    CXX="c++-$MAJOR_VERSION"

    verbosely "$CXX" -m32 -o"$tmpdir/cxx-m32" "$tmpdir/t.cxx"
    ok "compiled C++ for 32-bit via -m32"
    verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/cxx-m32"
    ok "it is really 32-bit"
    verbosely "$tmpdir/cxx-m32"
    ok "ran C++ for 32-bit"

    CC="${i386_gnu_type}-gcc-$MAJOR_VERSION"
    CXX="${i386_gnu_type}-g++-$MAJOR_VERSION"

    verbosely "$CC" -o"$tmpdir/c-i386" "$tmpdir/t.c"
    ok "compiled C for 32-bit via architecture prefix"
    verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/c-i386"
    ok "it is really 32-bit"
    verbosely "$tmpdir/c-i386"
    ok "ran C for 32-bit"

    verbosely "$CXX" -o"$tmpdir/cxx-i386" "$tmpdir/t.cxx"
    ok "compiled C++ for 32-bit via architecture prefix"
    verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/cxx-i386"
    ok "it is really 32-bit"
    verbosely "$tmpdir/cxx-i386"
    ok "ran C++ for 32-bit"

    CXX="${i386_gnu_type}-c++-$MAJOR_VERSION"

    verbosely "$CXX" -o"$tmpdir/cxx-i386" "$tmpdir/t.cxx"
    ok "compiled C++ for 32-bit via architecture prefix"
    verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/cxx-i386"
    ok "it is really 32-bit"
    verbosely "$tmpdir/cxx-i386"
    ok "ran C++ for 32-bit"
fi

# Check that we can also enable the backport by prepending it to the PATH,
# which makes the unversioned names gcc, g++, etc. select it
path="/usr/lib/gcc-14/bin:$PATH"

for suffix in "" "-$MAJOR_VERSION"; do
    for prefix in "" "${DEB_BUILD_GNU_TYPE}-"; do
        CC="${prefix}gcc${suffix}"
        CXX="${prefix}g++${suffix}"

        verbosely env PATH="$path" "$CC" -o"$tmpdir/${prefix}c" "$tmpdir/t.c"
        ok "compiled C"
        verbosely file "$tmpdir/${prefix}c"
        verbosely "$tmpdir/${prefix}c"
        ok "ran C"

        verbosely env PATH="$path" "$CXX" -o"$tmpdir/${prefix}cxx" "$tmpdir/t.cxx"
        ok "compiled C++"
        verbosely file "$tmpdir/${prefix}cxx"
        verbosely "$tmpdir/${prefix}cxx"
        ok "ran C++"

        CXX="${prefix}c++${suffix}"
        verbosely env PATH="$path" "$CXX" -o"$tmpdir/${prefix}cxx" "$tmpdir/t.cxx"
        ok "compiled C++"
        verbosely file "$tmpdir/${prefix}cxx"
        verbosely "$tmpdir/${prefix}cxx"
        ok "ran C++"
    done

    if [ "$arch" = amd64 ]; then
        CC="gcc${suffix}"
        CXX="g++${suffix}"

        verbosely env PATH="$path" "$CC" -m32 -o"$tmpdir/c-m32" "$tmpdir/t.c"
        ok "compiled C for 32-bit via -m32"
        verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/c-m32"
        ok "it is really 32-bit"
        verbosely "$tmpdir/c-m32"
        ok "ran C for 32-bit"

        verbosely env PATH="$path" "$CXX" -m32 -o"$tmpdir/cxx-m32" "$tmpdir/t.cxx"
        ok "compiled C++ for 32-bit via -m32"
        verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/cxx-m32"
        ok "it is really 32-bit"
        verbosely "$tmpdir/cxx-m32"
        ok "ran C++ for 32-bit"

        CXX="c++${suffix}"

        verbosely env PATH="$path" "$CXX" -m32 -o"$tmpdir/cxx-m32" "$tmpdir/t.cxx"
        ok "compiled C++ for 32-bit via -m32"
        verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/cxx-m32"
        ok "it is really 32-bit"
        verbosely "$tmpdir/cxx-m32"
        ok "ran C++ for 32-bit"

        CC="${i386_gnu_type}-gcc${suffix}"
        CXX="${i386_gnu_type}-g++${suffix}"

        verbosely env PATH="$path" "$CC" -o"$tmpdir/c-i386" "$tmpdir/t.c"
        ok "compiled C for 32-bit via architecture prefix"
        verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/c-i386"
        ok "it is really 32-bit"
        verbosely "$tmpdir/c-i386"
        ok "ran C for 32-bit"

        verbosely env PATH="$path" "$CXX" -o"$tmpdir/cxx-i386" "$tmpdir/t.cxx"
        ok "compiled C++ for 32-bit via architecture prefix"
        verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/cxx-i386"
        ok "it is really 32-bit"
        verbosely "$tmpdir/cxx-i386"
        ok "ran C++ for 32-bit"

        CXX="${i386_gnu_type}-c++${suffix}"

        verbosely env PATH="$path" "$CXX" -o"$tmpdir/cxx-i386" "$tmpdir/t.cxx"
        ok "compiled C++ for 32-bit via architecture prefix"
        verbosely grep -F /lib/ld-linux.so.2 "$tmpdir/cxx-i386"
        ok "it is really 32-bit"
        verbosely "$tmpdir/cxx-i386"
        ok "ran C++ for 32-bit"
    fi
done

done_testing

# vim:set sw=4 sts=4 et ft=sh:
