defconfig_builder.sh 10.4 KB
#!/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"
}

list_all_targets() {

	TMP_MAP=$(mktemp -t $TMP_TEMPLATE)
	cat "$DEFCONFIG_MAP_FILE" > "$TMP_MAP"
	while true;
	do
		CONFIG_FILE=
		CONFIG_FRAGMENTS=

		BT_TEMP=$(head -n 1 "$TMP_MAP" | awk '{print$4}')
		BUILD_DETAILS=$(head -n 1 "$TMP_MAP")
		if [ -z "$BUILD_DETAILS" ]; then
			break
		fi
		check_for_config_existance
		sed -i "1d" "$TMP_MAP"
	done
	rm "$TMP_MAP"
	cat "$BUILD_TYPE_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 or a match for $CHOSEN_BUILD_TYPE"
		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" | awk '{print$4}')
			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
			echo "Maybe try one of the following:"
			list_all_targets
		else
			echo "Did you mean any of the following?"
			cat "$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.
	-l - List all buildable defconfig options

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:l" OPTION
do
	case $OPTION in
		w)
			WORKING_PATH=$OPTARG;;
		t)
			CHOSEN_BUILD_TYPE=$OPTARG;;
		l)
			LIST_TARGETS="y";;
		?)
			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

BUILD_TYPE_FILE=$(mktemp -t $TMP_TEMPLATE)

if [ ! -z "$LIST_TARGETS" ]; then
	echo "The following are a list of buildable defconfigs:"
	list_all_targets
	exit 0
fi

PROCESSOR_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