#!/bin/sh
# Shell script to build GNU Make in the absence of any 'make' program.

# Copyright (C) 1993-2024 Free Software Foundation, Inc.
# This file is part of GNU Make.
#
# GNU Make 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.
#
# GNU Make 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 <https://www.gnu.org/licenses/>.

# Get configure-generated values
. ./build.cfg

die () { echo "$*" 1>&2; exit 1; }
usage () { echo "$0 [-k]"; exit $1; }

keep_going=false

: ${OUTDIR:=.}
OUTLIB="$OUTDIR/lib"

# Directory to find libraries in for '-lXXX'.
libdir=$exec_prefix/lib
# Directory to search by default for included makefiles.
includedir=$prefix/include

localedir=$prefix/share/locale

defines="-DLOCALEDIR=\"$localedir\" -DLIBDIR=\"$libdir\" -DINCLUDEDIR=\"$includedir\""

# Look up a make variable value.
# It can handle simple recursion where variables are separate words.
# Print the value to stdout.
get_mk_var ()
{
  v=$(sed -e :a -e '/\\$/N; s/\\\n//; ta' "$1" | sed -n "s=^ *$2 *\= *==p")
  for w in $v; do
    case $w in
      (\$[\(\{]*[\)\}]) w=${w#\$[\(\{]}; (get_mk_var "$1" "${w%[\)\}]}") ;;
      (*) echo "$w" ;;
    esac
  done
}

# Compile source files.  Object files are put into $objs.
compile ()
{
  success=true
  objs=
  for ofile in "$@"; do
    # We should try to use a Makefile variable like libgnu_a_SOURCES or
    # something but just hardcode it.
    file="${ofile%.$OBJEXT}.c"
    case $file in
        (lib/libgnu_a-*.c) file="lib/${file#lib/libgnu_a-}" ;;
    esac
    echo "compiling $file..."
    of="$OUTDIR/$ofile"
    mkdir -p "${of%/*}" || exit 1
    if $CC $cflags $CPPFLAGS $CFLAGS -c -o "$of" "$top_srcdir/$file"; then
        : worked
    else
        $keep_going || die "Compilation failed."
        success=false
    fi

    objs="${objs:+$objs }$of"
  done

  $success
}

# Use config.status to convert a .in file.  Output file is put into $out.
# $out will be empty if no conversion was needed.
convert ()
{
  out=
  base=$1
  var="GENERATE_$(echo $base | tr 'a-z./+' A-Z__X)"

  # Is this file disabled?
  grep "${var}_FALSE\"]=\"\"" config.status >/dev/null && return 0

  # If there's no .in file then no conversion needed
  in="$top_srcdir/lib/$(echo ${base%.*}.in.${base##*.} | tr / _)"
  test -f "$in" || return 0

  # Not disabled, so create it
  out="$OUTLIB/$base"
  mkdir -p "${out%/*}"

  # First perform the normal replacements, using config.status
  sed -e 's|@GUARD_PREFIX@|GL|g' \
      -e 's/@GNULIB_UNISTD_H_GETOPT@/0/g' \
      "$in" > "${out}_"
  ./config.status --file "${out}__:${out}_"
  int="${out}__"

  # Then see if there any files we need to include.  Unfortunately there's no
  # algorithmic conversion so we just have to hard-code it.
  incls=$(sed -n 's/.*definitions* of \(_[^ $]*\).*/\1/p' "$in")

  for inc in $incls; do
    case $inc in
      (_GL_FUNCDECL_RPL) fn=$(get_mk_var lib/Makefile CXXDEFS_H) ;;
      (_GL_ARG_NONNULL)  fn=$(get_mk_var lib/Makefile ARG_NONNULL_H) ;;
      (_GL_WARN_ON_USE)  fn=$(get_mk_var lib/Makefile WARN_ON_USE_H) ;;
      (_Noreturn)        fn=$(get_mk_var lib/Makefile _NORETURN_H) ;;
      (*) echo "Unknown file replacement: $inc"; exit 1 ;;
    esac

    fn="$top_srcdir/lib/${fn##*/}"
    test -f "$fn" || { echo "Missing file: $fn"; exit 1; }

    sed "/definitions* of $inc/r $fn" "$int" > "${int}_"
    int=${int}_
  done

  # Done!
  mv "$int" "$out"
}

# Get source files provided from gnulib and convert to object files
LIBOBJS=
for lo in $( (get_mk_var lib/Makefile libgnu_a_OBJECTS; get_mk_var lib/Makefile libgnu_a_LIBADD) | sed "s=\$[\(\{]OBJEXT[\)\}]=$OBJEXT=g"); do
  LIBOBJS="${LIBOBJS:+$LIBOBJS }lib/$lo"
done

# Get object files from the Makefile
OBJS=$(get_mk_var Makefile make_OBJECTS | sed "s=\$[\(\{]OBJEXT[\)\}]=$OBJEXT=g")

while test -n "$1"; do
    case $1 in
        (-k) keep_going=true; shift ;;
        (--) shift; break ;;
        (-[h?]) usage 0 ;;
        (-*) echo "Unknown option: $1"; usage 1 ;;
    esac
done

test -z "$1" || die "Unknown argument: $*"

# Generate gnulib header files that would normally be created by make
set -e
for b in $(get_mk_var lib/Makefile BUILT_SOURCES); do
    convert $b
done
set +e

# Build the gnulib library
cflags="$DEFS -I$OUTLIB -Ilib -I$top_srcdir/lib -I$OUTDIR/src -Isrc -I$top_srcdir/src"
compile $LIBOBJS || die "Compilation failed."

echo "creating libgnu.a..."
$AR $ARFLAGS "$OUTLIB"/libgnu.a $objs || die "Archive of libgnu failed."

# Compile the source files into those objects.
cflags="$DEFS $defines -I$OUTDIR/src -Isrc -I$top_srcdir/src -I$OUTLIB -Ilib -I$top_srcdir/lib"
compile $OBJS || die "Compilation failed."

# Link all the objects together.
echo "linking make..."
$CC $CFLAGS $LDFLAGS -L"$OUTLIB" -o "$OUTDIR/makenew$EXEEXT" $objs -lgnu $LOADLIBES || die "Link failed."

mv -f "$OUTDIR/makenew$EXEEXT" "$OUTDIR/make$EXEEXT" || exit 1

echo done.
