#!/bin/sh

#  Multi-build script for testing compilation of all maintained
#  configs of GDB.

#  Copyright (C) 2002-2026 Free Software Foundation, Inc.

#  Contributed by Richard Earnshaw  (rearnsha@arm.com)

#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Make certain that the script is not running in an internationalized
# environment.
LANG=c ; export LANG
LC_ALL=c ; export LC_ALL

usage()
{
    cat <<EOF
Usage: gdb_mbuild.sh [ <options> ... ] <srcdir> <builddir>
 Options:
   -j <makejobs>  Run <makejobs> in parallel.  Passed to make.
		  On a single cpu machine, 2 is recommended.
   -k             Keep going.  Do not stop after the first build fails.
   --keep         Keep builds.  Do not remove each build when finished.
   -e <regexp>    Regular expression for selecting the targets to build.
   -f             Force rebuild.  Even rebuild previously built directories.
   -v             Be more (and more, and more) verbose.
 Arguments:
   <srcdir>       Source code directory.
   <builddir>     Build directory.
 Environment variables examined (with default if not defined):
   MAKE (make)"
EOF
    exit 1;
cat <<NOTYET
  -b <maxbuilds> Run <maxbuild> builds in parallel.
		 On a single cpu machine, 1 is recommended.
NOTYET
}

### COMMAND LINE OPTIONS

makejobs=
maxbuilds=1
keepgoing=
force=false
targexp=""
verbose=0
keep=false
while test $# -gt 0
do
    case "$1" in
    -j )
	# Number of parallel make jobs.
	shift
	test $# -ge 1 || usage
	makejobs="-j $1"
	;;
    -b | -c )
	# Number of builds to fire off in parallel.
	shift
	test $# -ge 1 || usage
	maxbuilds=$1
	;;
    -k )
	# Should we soldier on after the first build fails?
	keepgoing=-k
	;;
    --keep )
	keep=true
	;;
    -e )
	# A regular expression for selecting targets
	shift
	test $# -ge 1 || usage
	targexp="${targexp} -e ${1}"
	;;
    -f )
	# Force a rebuild
	force=true ;
	;;
    -v )
	# Be more, and more, and more, verbose
	verbose=`expr ${verbose} + 1`
	;;
    -* ) usage ;;
    *) break ;;
    esac
    shift
done


### COMMAND LINE PARAMETERS

if test $# -ne 2
then
    usage
fi

# Convert these to absolute directory paths.

# Where the sources live
srcdir=`cd $1 && /bin/pwd` || exit 1

# Where the builds occur
builddir=`cd $2 && /bin/pwd` || exit 1

### ENVIRONMENT PARAMETERS

# Version of make to use
make=${MAKE:-make}
MAKE=${make}
export MAKE


# Where to look for the list of targets to test
maintainers=${srcdir}/gdb/MAINTAINERS
if [ ! -r ${maintainers} ]
then
    echo Maintainers file ${maintainers} not found
    exit 1
fi

# Get the list of targets and the build options
alltarg=`cat ${maintainers} | tr -s '[\t]' '[ ]' | sed -n '
/^[ ]*[-a-z0-9\.]*[ ]*[(]*--target=.*/ !d
s/^.*--target=//
s/).*$//
h
:loop
  g
  /^[^ ]*,/ !b end
  s/,[^ ]*//
  p
  g
  s/^[^,]*,//
  h
b loop
:end
p
' | if test "${targexp}" = ""
then
    grep -v -e broken -e OBSOLETE
else
    grep ${targexp}
fi`


# Usage: fail <message> <test-that-should-succeed>.  Should the build
# fail?  If the test is true, and we don't want to keep going, print
# the message and shoot everything in sight and abort the build.

fail ()
{
    msg="$1" ; shift
    if test "$@"
    then
	echo "${target}: ${msg}"
	if test "${keepgoing}" != ""
	then
	    #exit 1
	    return 1
	else
	    kill $$
	    exit 1
	fi
    fi
}


# Usage: log <level> <logfile>.  Write standard input to <logfile> and
# stdout (if verbose >= level).

log ()
{
    if test ${verbose} -ge $1
    then
	tee $2
    else
	cat > $2
    fi
}



# Warn the user of what is coming, print the list of targets

echo "$alltarg"
echo ""


# For each target, configure, build and test it.

echo "$alltarg" | while read target gdbopts simopts
do

    trap "exit 1"  1 2 15
    dir=${builddir}/${target}

    # Should a scratch rebuild be forced, for perhaps the entire
    # build be skipped?

    if ${force}
    then
	echo forcing ${target} ...
	rm -rf ${dir}
    elif test -f ${dir}
    then
	echo "${target}"
	continue
    else
	echo ${target} ...
    fi

    # Did the previous configure attempt fail?  If it did
    # restart from scratch.

    if test -d ${dir} -a ! -r ${dir}/Makefile
    then
	echo ... removing partially configured ${target}
	rm -rf ${dir}
	if test -d ${dir}
	then
	    echo "${target}: unable to remove directory ${dir}"
	    exit 1
	fi
    fi

    # From now on, we're in this target's build directory

    mkdir -p ${dir}
    cd ${dir} || exit 1

    # Configure, if not already.  Should this go back to being
    # separate and done in parallel?

    if test ! -r Makefile
    then
	# Default SIMOPTS to GDBOPTS.
	test -z "${simopts}" && simopts="${gdbopts}"
	# The config options
	__target="--target=${target}"
	__enable_gdb_build_warnings=`test -z "${gdbopts}" \
	    || echo "--enable-gdb-build-warnings=${gdbopts}"`
	__enable_sim_build_warnings=`test -z "${simopts}" \
	    || echo "--enable-sim-build-warnings=${simopts}"`
	__configure="${srcdir}/configure \
	    ${__target} \
	    ${__enable_gdb_build_warnings} \
	    ${__enable_sim_build_warnings}"
	echo ... ${__configure}
	trap "echo Removing partially configured ${dir} directory ...; rm -rf ${dir}; exit 1" 1 2 15
	${__configure} 2>&1 | log 2 Config.log
	trap "exit 1"  1 2 15
    fi
    fail "configure failed" ! -r Makefile

    # Build, if not built.

    if test ! -x gdb/gdb -a ! -x gdb/gdb.exe
    then
	# Iff the build fails remove the final build target so that
	# the follow-on code knows things failed.  Stops the follow-on
	# code thinking that a failed rebuild succeeded (executable
	# left around from previous build).
	echo ... ${make} ${keepgoing} ${makejobs} ${target}
	( ${make} ${keepgoing} ${makejobs} all-gdb || rm -f gdb/gdb gdb/gdb.exe
	) 2>&1 | log 1 Build.log
    fi
    fail "compile failed" ! -x gdb/gdb -a ! -x gdb/gdb.exe

    # Check that the built GDB can at least print it's architecture.

    echo ... run ${target}
    rm -f core gdb.core ${dir}/gdb/x
    cat <<EOF > x
maint print architecture
quit
EOF
    ./gdb/gdb -batch -nx -x x 2>&1 | log 1 Gdb.log
    fail "gdb dumped core" -r core -o -r gdb.core
    fail "gdb printed no output" ! -s Gdb.log
    grep -e internal-error Gdb.log && fail "gdb panic" 1

    echo ... cleanup ${target}

    # Create a sed script that cleans up the output from GDB.
    rm -f mbuild.sed
    touch mbuild.sed || exit 1
    # Rules to replace <0xNNNN> with the corresponding function's
    # name.
    sed -n -e '/<0x0*>/d' -e 's/^.*<0x\([0-9a-f]*\)>.*$/0x\1/p' Gdb.log \
    | sort -u \
    | while read addr
    do
	func="`addr2line -f -e ./gdb/gdb -s ${addr} | sed -n -e 1p`"
	test ${verbose} -gt 0 && echo "${addr} ${func}" 1>&2
	echo "s/<${addr}>/<${func}>/g"
    done >> mbuild.sed
    # Rules to strip the leading paths off of file names.
    echo 's/"\/.*\/gdb\//"gdb\//g' >> mbuild.sed
    # Run the script
    sed -f mbuild.sed Gdb.log > Mbuild.log

    # Replace the build directory with a file as semaphore that stops
    # a rebuild. (should the logs be saved?)

    cd ${builddir}

    if ${keep}
    then
	:
    else
	rm -f ${target}.tmp
	mv ${target}/Mbuild.log ${target}.tmp
	rm -rf ${target}
	mv ${target}.tmp ${target}
    fi

    # Success!
    echo ... ${target} built

done

exit 0
