#!/bin/bash
#
#  defconfig_builder.sh
#
#  Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
#  ALL RIGHTS RESERVED
#
#  This script will perform a merge of config fragment files into a defconfig
#  based on a map file.  The map file defines the defconfig options that have
#  been tested by TI to boot and compile.
#
#  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 2 of the License.
#
#  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.
#
#  For more information type defconfig_builder.sh -?
#
# IMPORTANT NOTE: All modifications to this script must be verified by running
# 'shellcheck --shell=bash defconfig_builder.sh' See 'Installing' section in:
# https://github.com/koalaman/shellcheck/blob/master/README.md
# Also note that shellcheck must be at least at revision 0.3.3

DEBUG_CONFIG_TAG="debug_options"
CONFIG_FRAGMENT_TAG="config-fragment="
DISCLAIMER="\n*Please be advised that the Debug Option defconfigs may\nimpact \
performance and should only be used for debugging.\n"

# Template for temporary build files.. use PID to differentiate
TMP_PREFIX=ti_defconfig_builder_$$
TMP_TEMPLATE="$TMP_PREFIX"_XXXXX.tmp

set_working_directory() {
	# Sanity checkup kernel build location.
	if [ ! -d "$WORKING_PATH" ]; then
		WORKING_PATH=$(pwd)
	fi

	ORIGINAL_DIR=$(pwd)
	if [ "$ORIGINAL_DIR" != "$WORKING_PATH" ]; then
		cd "$WORKING_PATH"
		WORKING_PATH=$(pwd)
	fi

	TI_WORKING_PATH="$WORKING_PATH/ti_config_fragments"
	DEFCONFIG_KERNEL_PATH="$WORKING_PATH/arch/arm/configs"
	DEFCONFIG_MAP_FILE="$TI_WORKING_PATH/defconfig_map.txt"
}

prepare_for_exit() {
	D=$(dirname "$PROCESSOR_FILE")
	rm -f "$PROCESSOR_FILE"
	rm -f "$BUILD_TYPE_FILE"
	rm -f "$TEMP_TYPE_FILE"
	if [ -s "$OLD_CONFIG" ]; then
		mv "$OLD_CONFIG" "$WORKING_PATH"/.config
	fi
	# Clean everyone else up if we missed any
	rm -f "$D"/"$TMP_PREFIX"*.tmp
	exit
}

check_for_config_existance() {
	# Check to make sure that the config fragments exist
	TEMP_EXTRA_CONFIG_FILE=$(echo "$BUILD_DETAILS" | cut -d: -f6)
	if [ -z "$TEMP_EXTRA_CONFIG_FILE" ]; then
		CONFIG_FRAGMENTS=
	else
		for CONFIG_FRAGMENT_FILE in $TEMP_EXTRA_CONFIG_FILE;
		do
			# If we do already point to existing file, we are good.
			if [ -e "$CONFIG_FRAGMENT_FILE" ]; then
				CONFIG_FRAG="$CONFIG_FRAGMENT_FILE"
			else
				# Assume it is present in TI working path
				CONFIG_FRAG="$TI_WORKING_PATH/$CONFIG_FRAGMENT_FILE"
			fi
			if [ ! -e "$CONFIG_FRAG" ]; then
				CONFIG_FRAGMENTS="N/A"
			fi
		done
	fi

	if ! grep -qc "$BT_TEMP" "$BUILD_TYPE_FILE"; then
		# If the config file and config fragments are available
		# add it to the list.
		CONFIG_FILE=$(echo "$BUILD_DETAILS" | awk '{print$8}')
		if [ "$CONFIG_FILE" = "None" ]; then
			CONFIG_FILE=
		else
			if [ -e "$TI_WORKING_PATH""/""$CONFIG_FILE" ]; then
				CONFIG_FILE=
			fi
		fi
		# If the check for the config file and the config fragments
		# pass then these two variables should be empty.  If
		# they fail then they should be N/A.
		if [ -z "$CONFIG_FILE" -a -z "$CONFIG_FRAGMENTS" ]; then
			max_configs=$((max_configs+1))
			echo -e '\t'"$max_configs". "$BT_TEMP" >> "$BUILD_TYPE_FILE"
		fi
	fi
}

choose_build_type() {
	TEMP_BT_FILE=$(mktemp -t $TMP_TEMPLATE)
	TEMP_BUILD_FILE=$(mktemp -t $TMP_TEMPLATE)

	grep "$DEFCONFIG_FILTER" "$DEFCONFIG_MAP_FILE" | grep "^classification:" | awk '{print$4}' > "$TEMP_BUILD_FILE"

	max_configs=0
	while true;
	do
		CONFIG_FILE=
		CONFIG_FRAGMENTS=

		BT_TEMP=$(head -n 1 "$TEMP_BUILD_FILE")
		if [ -z "$BT_TEMP" ]; then
			break
		fi
		BUILD_DETAILS=$(grep -w "$BT_TEMP" "$DEFCONFIG_MAP_FILE")
		check_for_config_existance
		sed -i "1d" "$TEMP_BUILD_FILE"
	done

	NUM_OF_BUILDS=$(wc -l "$BUILD_TYPE_FILE" | awk '{print$1}')
	if [ "$NUM_OF_BUILDS" -eq 0 ]; then
		echo "Sorry no build targets for this configuration.  Are you on the right branch?"
		prepare_for_exit
	fi

	# Force the user to answer.  Maybe the user does not want to continue
	while true;
	do
		echo -e "Available ""$DEFCONFIG_FILTER"" defconfig build options:\n"
		cat "$BUILD_TYPE_FILE"
		echo ""
		read -p "Please enter the number of the defconfig to build or 'q' to exit: " REPLY

		if [ "$REPLY" = "q" -o "$REPLY" = "Q" ]; then
			prepare_for_exit
		elif ! [[ "$REPLY" =~ ^[0-9]+$ ]]; then
			echo -e "\n$REPLY is not a number of the defconfig.  Please try again!\n"
			continue
		elif [ "$REPLY" -gt '0' -a "$REPLY" -le "$NUM_OF_BUILDS" ]; then
			CHOSEN_BUILD_TYPE=$(grep -w "$REPLY" "$BUILD_TYPE_FILE" | awk '{print$2}')
			break
		else
			echo -e "\n'$REPLY' is not a valid choice. Please \
choose a value between '1' and '$max_configs':\n"
		fi
	done
	rm "$TEMP_BT_FILE"
	rm "$TEMP_BUILD_FILE"
}

get_build_details() {

	BUILD_DETAILS=$(grep -w "$CHOSEN_BUILD_TYPE" "$DEFCONFIG_MAP_FILE")
	if [ -z "$BUILD_DETAILS" ]; then
		echo "Cannot find the build type $CHOSEN_BUILD_TYPE"
		echo "Did you mean any of the following?"
		TEMP_BUILD_FILE=$(mktemp -t $TMP_TEMPLATE)
		grep "$CHOSEN_BUILD_TYPE" "$DEFCONFIG_MAP_FILE" > "$TEMP_BUILD_FILE"
		while true;
		do
			CONFIG_FILE=
			CONFIG_FRAGMENTS=

			BT_TEMP=$(head -n 1 "$TEMP_BUILD_FILE")
			if [ -z "$BT_TEMP" ]; then
				break
			fi
			BUILD_DETAILS=$(grep -w "$BT_TEMP" "$DEFCONFIG_MAP_FILE")
			check_for_config_existance
			sed -i "1d" "$TEMP_BUILD_FILE"
		done
		rm -rf "$TEMP_BUILD_FILE"

		NUM_OF_BUILDS=$(wc -l "$BUILD_TYPE_FILE" | awk '{print$1}')
		if [ "$NUM_OF_BUILDS" -eq 0 ]; then
			awk '{if ($2 == "$BUILD_TYPE_TAG") print $4;}' "$DEFCONFIG_MAP_FILE"
		else
			awk '{print $5}' "$BUILD_TYPE_FILE"
		fi

		return 1
	fi

	DEFCONFIG=$(echo "$BUILD_DETAILS" | awk '{print$6}')
	DEFCONFIG="$DEFCONFIG_KERNEL_PATH""/""$DEFCONFIG"
	CONFIG_FILE=$(echo "$BUILD_DETAILS" | awk '{print$8}')
	# There may be a need to just build with the config fragments themselves
	if [ "$CONFIG_FILE" = "None" ]; then
		CONFIG_FILE=
	fi

	if [ ! -e "$TI_WORKING_PATH/$CONFIG_FILE" ]; then
		echo "$TI_WORKING_PATH/$CONFIG_FILE does not exist"
		return 1
	fi

	TEMP_EXTRA_CONFIG_FILE=$(echo "$BUILD_DETAILS" | cut -d: -f6)
	for CONFIG_FRAGMENT_FILE in $TEMP_EXTRA_CONFIG_FILE;
	do
		# If we do already point to existing file, we are good.
		if [ -e "$CONFIG_FRAGMENT_FILE" ]; then
			CONFIG_FRAG="$CONFIG_FRAGMENT_FILE"
		else
			# Assume it is present in TI working path
			CONFIG_FRAG="$TI_WORKING_PATH/$CONFIG_FRAGMENT_FILE"
		fi
		if [ -e "$CONFIG_FRAG" ]; then
			EXTRA_CONFIG_FILE="$EXTRA_CONFIG_FILE $CONFIG_FRAG"
		else
			echo "$CONFIG_FRAG" does not exist
		fi
	done
}

build_defconfig() {

	if [ ! -z "$CONFIG_FILE" -a -e "$TI_WORKING_PATH/$CONFIG_FILE" ]; then
		CONFIGS=$(grep "$CONFIG_FRAGMENT_TAG" "$TI_WORKING_PATH/$CONFIG_FILE" | cut -d= -f2)
	fi

	"$WORKING_PATH"/scripts/kconfig/merge_config.sh -m -r "$DEFCONFIG" \
		"$CONFIGS" "$EXTRA_CONFIG_FILE" > /dev/null

	if [ "$?" = "0" ];then
		echo "Creating defconfig file ""$WORKING_PATH""/arch/arm/configs/""$CHOSEN_BUILD_TYPE"_defconfig
		mv .config "$DEFCONFIG_KERNEL_PATH"/"$CHOSEN_BUILD_TYPE"_defconfig
	else
		echo "Defconfig creation failed"
		return 1
	fi
}

choose_defconfig_type() {

	TEMP_TYPE_FILE=$(mktemp -t $TMP_TEMPLATE)

	TYPE_FILE=$(awk '{print$2}' "$DEFCONFIG_MAP_FILE" | sort -u | grep -i "SDK_")

	max_types=0
	for TYPE_TMP in $TYPE_FILE;
	do
		max_types=$((max_types+1))
		echo -e '\t' "$max_types." "$TYPE_TMP" >> "$TEMP_TYPE_FILE"
	done
	echo >> "$TEMP_TYPE_FILE"

	while true;
	do
		cat "$TEMP_TYPE_FILE"
		read -p "Please choose a defconfig type to build for or 'q' to exit: " REPLY
		if [ "$REPLY" = "q" -o "$REPLY" = "Q" ]; then
			prepare_for_exit
		elif ! [[ "$REPLY" =~ ^[0-9]+$ ]]; then
			echo -e "\n'$REPLY' is not a number for the build type.  Please try again!\n"
			continue
		elif [ "$REPLY" -gt '0' -a "$REPLY" -le "$max_types" ]; then
			REPLY="$REPLY""."
			DEFCONFIG_FILTER=$(awk '{if ($1 == "'"$REPLY"'") print $2;}' "$TEMP_TYPE_FILE")
			break
		else
			echo -e "\n'$REPLY' is not a valid choice. Please \
choose a value between '1' and '$max_types':\n"
		fi
	done

	DEBUG_BUILD=$(grep "$DEFCONFIG_FILTER" "$DEFCONFIG_MAP_FILE" | grep -wc "$DEBUG_CONFIG_TAG" )
	if [ "$DEBUG_BUILD" -gt '0' ]; then
		echo -e "$DISCLAIMER"
	fi
}

usage() {
cat << EOF

This script will perform a merge of config fragment files into a defconfig
based on a map file.  The map file defines the defconfig options that have
been tested by TI to boot and compile.

Optional:
	-w - Location of the TI Linux kernel
	-t - Indicates the type of defconfig to build.  This will force the
	     defconfig to build without user interaction.

Command line example to generate the TI SDK AM335x processor defconfig automatically
without user interaction:

	ti_config_fragments/defconfig_builder.sh -t ti_sdk_am3x_release

Command line Example if building from the ti_config_fragments directory:
	defconfig_builder.sh -w ../.

User interactive command line example:
	ti_config_fragments/defconfig_builder.sh
EOF
}

#########################################
# Script Start
#########################################
while getopts "?w:t:" OPTION
do
	case $OPTION in
		w)
			WORKING_PATH=$OPTARG;;
		t)
			CHOSEN_BUILD_TYPE=$OPTARG;;
		?)
			usage
			exit;;
     esac
done

trap prepare_for_exit SIGHUP EXIT SIGINT SIGTERM

set_working_directory

if [ ! -e "$DEFCONFIG_MAP_FILE" ]; then
	echo "No defconfig map file found"
	exit 1
fi

PROCESSOR_FILE=$(mktemp -t $TMP_TEMPLATE)
BUILD_TYPE_FILE=$(mktemp -t $TMP_TEMPLATE)
OLD_CONFIG=$(mktemp -t $TMP_TEMPLATE)
if [ -f "$WORKING_PATH"/.config ]; then
	mv "$WORKING_PATH"/.config "$OLD_CONFIG"
fi

if [ ! -z "$CHOSEN_BUILD_TYPE" ]; then
	get_build_details
	if [ "$?" -gt 0 ]; then
		exit 1
	fi

	build_defconfig
	if [ "$?" -gt 0 ]; then
		exit 1
	fi
	exit 0
fi

choose_defconfig_type

choose_build_type
get_build_details

build_defconfig
