#!/bin/bash

#
# edk2-build.sh: evolution of uefi-build.sh for edk2-platforms
#

unset MAKEFLAGS  # BaseTools not safe to build parallel, prevent env overrides

TOOLS_DIR="`dirname $0`"
TOOLS_DIR="`readlink -f \"$TOOLS_DIR\"`"
export TOOLS_DIR
. "$TOOLS_DIR"/common-functions
PLATFORM_CONFIG="-c $TOOLS_DIR/edk2-platforms.config"
ARCH=
VERBOSE=0                  # Override with -v
ATF_DIR=
TOS_DIR=
TOOLCHAIN="gcc"            # Override with -T
WORKSPACE=
EDK2_DIR=
PLATFORMS_DIR=
NON_OSI_DIR=
IMPORT_OPENSSL=TRUE
OPENSSL_CONFIGURED=FALSE

# Number of threads to use for build
export NUM_THREADS=$((`getconf _NPROCESSORS_ONLN` + `getconf _NPROCESSORS_ONLN`))

function do_build
{
	PLATFORM_ARCH=`echo $board | cut -s -d: -f2`
	if [ -n "$PLATFORM_ARCH" ]; then
		board=`echo $board | cut -d: -f1`
	else
		PLATFORM_ARCH="`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG -p $board get -o arch`"
	fi
	PLATFORM_NAME="`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG -p $board get -o longname` ($PLATFORM_ARCH)"
	if [ -z "$PLATFORM_ARCH" ]; then
		echo "Unknown target architecture - aborting!" >&2
		return 1
	fi
	PLATFORM_PREBUILD_CMDS="`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG -p $board get -o prebuild_cmds`"
	PLATFORM_BUILDFLAGS="`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG -p $board get -o buildflags`"
	PLATFORM_BUILDFLAGS="$PLATFORM_BUILDFLAGS ${EXTRA_OPTIONS[@]}"
	PLATFORM_BUILDCMD="`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG -p $board get -o buildcmd`"
	PLATFORM_DSC="`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG -p $board get -o dsc`"
	PLATFORM_PACKAGES_PATH=""
	COMPONENT_INF="`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG -p $board get -o inf`"

	TEMP_PACKAGES_PATH="$GLOBAL_PACKAGES_PATH:`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG -p $board get -o packages_path`"
	IFS=:
	for path in "$TEMP_PACKAGES_PATH"; do
		case "$path" in
			/*)
				PLATFORM_PACKAGES_PATH="$PLATFORM_PACKAGES_PATH:$path"
			;;
			*)
				PLATFORM_PACKAGES_PATH="$PLATFORM_PACKAGES_PATH:$PWD/$path"
			;;
	        esac
	done
	unset IFS

	if [ $VERBOSE -eq 1 ]; then
		echo "Setting build parallellism to $NUM_THREADS processes"
		echo "PLATFORM_NAME=$PLATFORM_NAME"
		echo "PLATFORM_PREBUILD_CMDS=$PLATFORM_PREBUILD_CMDS"
		echo "PLATFORM_BUILDFLAGS=$PLATFORM_BUILDFLAGS"
		echo "PLATFORM_BUILDCMD=$PLATFORM_BUILDCMD"
		echo "PLATFORM_DSC=$PLATFORM_DSC"
		echo "PLATFORM_ARCH=$PLATFORM_ARCH"
		echo "PLATFORM_PACKAGES_PATH=$PLATFORM_PACKAGES_PATH"
	fi

	set_cross_compile
	CROSS_COMPILE="$TEMP_CROSS_COMPILE"

	echo "Building $PLATFORM_NAME - $PLATFORM_ARCH"
	echo "CROSS_COMPILE=\"$TEMP_CROSS_COMPILE\""
	echo "$board"_BUILDFLAGS="'$PLATFORM_BUILDFLAGS'"

	if [ "$TARGETS" == "" ]; then
		TARGETS=( RELEASE )
	fi

	case $TOOLCHAIN in
		"gcc")
			PLATFORM_TOOLCHAIN=`get_gcc_version "$CROSS_COMPILE"gcc`
			;;
		"clang")
			PLATFORM_TOOLCHAIN=`get_clang_version clang`
			;;
		*)
			# Use command-line specified profile directly
			PLATFORM_TOOLCHAIN=$TOOLCHAIN
			;;
	esac
	echo "PLATFORM_TOOLCHAIN is ${PLATFORM_TOOLCHAIN}"

	export ${PLATFORM_TOOLCHAIN}_${PLATFORM_ARCH}_PREFIX=$CROSS_COMPILE
	echo "Toolchain prefix: ${PLATFORM_TOOLCHAIN}_${PLATFORM_ARCH}_PREFIX=$CROSS_COMPILE"

	export PACKAGES_PATH="$PLATFORM_PACKAGES_PATH"
	for target in "${TARGETS[@]}" ; do
		if [ X"$PLATFORM_PREBUILD_CMDS" != X"" ]; then
			echo "Run pre-build commands:"
			if [ $VERBOSE -eq 1 ]; then
				echo "  ${PLATFORM_PREBUILD_CMDS}"
			fi
			eval ${PLATFORM_PREBUILD_CMDS}
		fi

		if [ -n "$COMPONENT_INF" ]; then
			# Build a standalone component
			if [ $VERBOSE -eq 1 ]; then
				echo "build -n $NUM_THREADS -a \"$PLATFORM_ARCH\" -t ${PLATFORM_TOOLCHAIN} -p \"$PLATFORM_DSC\"" \
					"-m \"$COMPONENT_INF\" -b "$target" ${PLATFORM_BUILDFLAGS}"
			fi
			build -n $NUM_THREADS -a "$PLATFORM_ARCH" -t ${PLATFORM_TOOLCHAIN} -p "$PLATFORM_DSC" \
				-m "$COMPONENT_INF" -b "$target" ${PLATFORM_BUILDFLAGS}
		else
			# Build a platform
			if [ $VERBOSE -eq 1 ]; then
				echo "build -n $NUM_THREADS -a \"$PLATFORM_ARCH\" -t ${PLATFORM_TOOLCHAIN} -p \"$PLATFORM_DSC\"" \
					"-b "$target" ${PLATFORM_BUILDFLAGS}"
			fi
			build -n $NUM_THREADS -a "$PLATFORM_ARCH" -t ${PLATFORM_TOOLCHAIN} -p "$PLATFORM_DSC" \
				-b "$target" ${PLATFORM_BUILDFLAGS}
		fi

		RESULT=$?
		if [ $RESULT -eq 0 ]; then
			if [ X"$TOS_DIR" != X"" ]; then
				pushd $TOS_DIR >/dev/null
				if [ $VERBOSE -eq 1 ]; then
					echo "$TOOLS_DIR/tos-build.sh -e "$EDK2_DIR" -t "$target"_${PLATFORM_TOOLCHAIN} $board"
				fi
				$TOOLS_DIR/tos-build.sh -e "$EDK2_DIR" -t "$target"_${PLATFORM_TOOLCHAIN} $board
				RESULT=$?
				popd >/dev/null
			fi
		fi
		if [ $RESULT -eq 0 ]; then
			if [ X"$ATF_DIR" != X"" ]; then
				pushd $ATF_DIR >/dev/null
				if [ $VERBOSE -eq 1 ]; then
					echo "$TOOLS_DIR/atf-build.sh -e "$EDK2_DIR" -t "$target"_${PLATFORM_TOOLCHAIN} $board"
				fi
				$TOOLS_DIR/atf-build.sh -e "$EDK2_DIR" -t "$target"_${PLATFORM_TOOLCHAIN} $board
				RESULT=$?
				popd >/dev/null
			fi
		fi
		result_log $RESULT "$PLATFORM_NAME $target"
	done
	unset PACKAGES_PATH
}


function configure_paths
{
	WORKSPACE="$PWD"

	# Check to see if we are in a UEFI repository
	# refuse to continue if we aren't
	if [ ! -d "$EDK2_DIR"/BaseTools ]
	then
		if [ -d "$PWD"/edk2/BaseTools ]; then
			EDK2_DIR="$PWD"/edk2
		else
			echo "ERROR: can't locate the edk2 directory" >&2
			echo "       please specify -e/--edk2-dir" >&2
			exit 1
		fi
	fi

	GLOBAL_PACKAGES_PATH="$EDK2_DIR"

	# locate edk2-platforms
	if [ -z "$PLATFORMS_DIR" -a -d "$PWD"/edk2-platforms ]; then
		PLATFORMS_DIR="$PWD"/edk2-platforms
	fi
	if [ -n "$PLATFORMS_DIR" ]; then
		GLOBAL_PACKAGES_PATH="$GLOBAL_PACKAGES_PATH:$PLATFORMS_DIR"
	fi

	# locate edk2-non-osi
	if [ -z "$NON_OSI_DIR" -a -d "$PWD"/edk2-non-osi ]; then
		NON_OSI_DIR="$PWD"/edk2-non-osi
	fi
	if [ -n "$NON_OSI_DIR" ]; then
		GLOBAL_PACKAGES_PATH="$GLOBAL_PACKAGES_PATH:$NON_OSI_DIR"
	fi

	# locate arm-trusted-firmware
	if [ -z "$ATF_DIR" -a -d "$PWD"/arm-trusted-firmware ]; then
		ATF_DIR="$PWD"/arm-trusted-firmware
	fi

	export WORKSPACE
}


function prepare_build
{
	get_build_arch
	export ARCH=$BUILD_ARCH

	export ARCH
	cd $EDK2_DIR
	PACKAGES_PATH=$GLOBAL_PACKAGES_PATH . edksetup.sh --reconfig
	if [ $? -ne 0 ]; then
		echo "Sourcing edksetup.sh failed!" >&2
		exit 1
	fi
	if [ $VERBOSE -eq 1 ]; then
		echo "Building BaseTools"
	fi
	make -C BaseTools
	RET=$?
	cd -
	if [ $RET -ne 0 ]; then
		echo " !!! BaseTools failed to build !!! " >&2
		exit 1
	fi

	if [ "$IMPORT_OPENSSL" = "TRUE" ]; then
		cd $EDK2_DIR
		import_openssl
		if [ $? -ne 0 ]; then
			echo "Importing OpenSSL failed - aborting!" >&2
			echo "  specify --no-openssl to attempt build anyway." >&2
			exit 1
		fi
		cd $WORKSPACE
	fi
}


function usage
{
	echo "usage:"
	echo -n "uefi-build.sh [-b DEBUG | RELEASE] [ all "
	for board in "${boards[@]}" ; do
	    echo -n "| $board "
	done
	echo "]"
	printf "%8s\tbuild %s\n" "all" "all supported platforms"
	for board in "${boards[@]}" ; do
		PLATFORM_NAME="`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG -p $board get -o longname`"
		printf "%8s\tbuild %s\n" "$board" "${PLATFORM_NAME}"
	done
}

#
# Since we do a command line validation on whether specified platforms exist or
# not, do a first pass of command line to see if there is an explicit config
# file there to read valid platforms from.
#
commandline=( "$@" )
i=0
for arg;
do
	if [ $arg == "-c" ]; then
		FILE_ARG=${commandline[i + 1]}
		if [ ! -f "$FILE_ARG" ]; then
			echo "ERROR: configuration file '$FILE_ARG' not found" >&2
			exit 1
		fi
		case "$FILE_ARG" in
			/*)
				PLATFORM_CONFIG="-c $FILE_ARG"
			;;
			*)
				PLATFORM_CONFIG="-c `readlink -f \"$FILE_ARG\"`"
			;;
		esac
		echo "Platform config file: '$FILE_ARG'"
	fi
	i=$(($i + 1))
done

export PLATFORM_CONFIG

builds=()
boards=()
boardlist="`$TOOLS_DIR/parse-platforms.py $PLATFORM_CONFIG shortlist`"
for board in $boardlist; do
    boards=(${boards[@]} $board)
done

NUM_TARGETS=0

while [ "$1" != "" ]; do
	case $1 in
		-1)     # Disable build parallellism
			NUM_THREADS=1
			;;
		-a | --arm-tf-dir)
			shift
			ATF_DIR="`readlink -f $1`"
			;;
		-c)     # Already parsed above - skip this + option
			shift
			;;
		-b | --build-target)
			shift
			echo "Adding Build target: $1"
			TARGETS=(${TARGETS[@]} $1)
			;;
		-D)     # Pass through as -D option to 'build'
			shift
			echo "Adding Macro: -D $1"
			EXTRA_OPTIONS=(${EXTRA_OPTIONS[@]} "-D" $1)
			;;
		-e | --edk2-dir)
			shift
			export EDK2_DIR="`readlink -f $1`"
			;;
		-h | --help)
			usage
			exit
			;;
		--no-openssl)
			IMPORT_OPENSSL=FALSE
			;;
		-n | --non-osi-dir)
			shift
			NON_OSI_DIR="`readlink -f $1`"
			;;
		-p | --platforms-dir)
			shift
			PLATFORMS_DIR="`readlink -f $1`"
			;;
		-s | --tos-dir)
			shift
			export TOS_DIR="`readlink -f $1`"
			;;
		-T)     # Set specific toolchain tag, or clang/gcc for autoselection
			shift
			echo "Setting toolchain tag to '$1'"
			TOOLCHAIN="$1"
			;;
		-v)
			VERBOSE=1
			;;
		all)    # Add all targets in configuration file to list
			builds=(${boards[@]})
			NUM_TARGETS=$(($NUM_TARGETS + 1))
			;;
		*)      # Try to match target in configuration file, add to list
			MATCH=0
			for board in "${boards[@]}" ; do
				if [ "`echo $1 | cut -d: -f1`" == $board ]; then
					MATCH=1
					builds=(${builds[@]} "$1")
					break
				fi
			done

			if [ $MATCH -eq 0 ]; then
				echo "unknown arg $1"
				usage
				exit 1
			fi
			NUM_TARGETS=$(($NUM_TARGETS + 1))
			;;
	esac
	shift
done

if [ $NUM_TARGETS -le  0 ]; then
	echo "No targets specified - exiting!" >&2
	exit 0
fi

export VERBOSE

configure_paths

prepare_build

if [[ "${EXTRA_OPTIONS[@]}" != *"FIRMWARE_VER"* ]]; then
	if test -d .git && head=`git rev-parse --verify --short HEAD 2>/dev/null`; then
		FIRMWARE_VER=`git rev-parse --short HEAD`
		if ! git diff-index --quiet HEAD --; then
			FIRMWARE_VER="${FIRMWARE_VER}-dirty"
		fi
		EXTRA_OPTIONS=( ${EXTRA_OPTIONS[@]} "-D" FIRMWARE_VER=$FIRMWARE_VER )
		if [ $VERBOSE -eq 1 ]; then
			echo "FIRMWARE_VER=$FIRMWARE_VER"
			echo "EXTRA_OPTIONS=$EXTRA_OPTIONS"
		fi
	fi
fi

for board in "${builds[@]}" ; do
	do_build
done

result_print