#!/bin/bash


#===----------------------------------------------------------------------===#
#==== Copyright (c) 2015 Advanced Micro Devices, Inc. All rights reserved.
#
# Developed by: Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# with the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimers.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimers in the documentation
# and/or other materials provided with the distribution.
#
# Neither the names of Advanced Micro Devices, Inc., nor the names of its
# contributors may be used to endorse or promote products derived from this
# Software without specific prior written permission.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
# THE SOFTWARE.
#===----------------------------------------------------------------------===#

gCompilerUt=""
gCompilerPpUt=""
gInput=""

gGlibcVersion=2.17
gCurrentGlibcVersion=0.0

gFailCount=0
gFailTestStatus=""
gPassCount=0
gPassTestStatus=""

declare -a glibalm

glibalm=(libalm.so)
glibomp=(libomp.so)
glibamdalloc=(libamdalloc.so)

gDynamicLibMissing=0

glibtinfo="libtinfo.so.5"
glibtinfoStr="libtinfo.so"

glibz="libz.so.1"
glibzStr="libz.so"

g_crt_file=(crt1.o crti.o crtn.o)
g_libc=(libc.so)
g_libm=(libm.so)

printTestStatusAndExit()
{
	if [[ $gFailTestStatus != "" ]];then
		retVal=1
		echo -e "\nFailing Checks:"
		echo -e $gFailTestStatus
		if [[ $gDynamicLibMissing -eq 1 ]];then
			echo -e "\tExternal libraries in above failing checks/warnings are suggested to get best scores with AOCC"
			echo -e "\tIf your SPEC CPU2017 config does not use above external libraries then please ignore above failing checks/warnings related to these libraries.\n"
		fi
	else
		retVal=0
		echo -e "\nCheck:PASSED"
	fi

	if [[ $gPassTestStatus != "" ]];then
		echo -e "Passing Checks:"
		echo -e $gPassTestStatus
	fi

	exit $retVal
}

Fail_OpenMP()
{
	gFailCount=$((gFailCount+1))
	gFailTestStatus="${gFailTestStatus}\t$gFailCount)WARNING: $1 OpenMP library is required in both LIBRARY_PATH and LD_LIBRARY_PATH\n\tWithout this compilation will fail for $1 source code which uses OpenMP library\n\tMake sure that both LIBRARY_PATH and LD_LIBRARY_PATH have folder containing OpenMP library files\n\n"
}

Pass_OpenMP()
{
	l_loc=$1
	gPassCount=$((gPassCount+1))
	gPassTestStatus="${gPassTestStatus}\t$gPassCount) AMD OpenMP library ($2) => $l_loc\n"
}

Test_OpenMP()
{
local flagPass_LD_lib_path=0;
local flagPass_lib_path=0;
local l_file=""

    if [[ "$1" == 32-bit ]]; then
        m32orm64key="32-bit"
    else
        m32orm64key="64-bit"
    fi


	ldPathAll=$LD_LIBRARY_PATH
	old_IFS=$IFS
	IFS=':';allFolders=$ldPathAll
	for folder in ${allFolders[@]};
	do
		if [[ -d "$folder" ]];then
		{
			for lib in ${glibomp[@]};
			do
				if [[ (-e "${folder}/${lib}") ]];then
                                        arch=$(file ${folder}/${lib}| grep $m32orm64key )
					if [[ $arch == *"$m32orm64key"* ]]; then
					    l_file="${folder}/${lib}"
					    flagPass_LD_lib_path=1
					    break 2
					fi
				fi
			done
		}
		fi
	done
	IFS=$old_IFS

	ldPathAll=$LIBRARY_PATH
	old_IFS=$IFS
	IFS=':';allFolders=$ldPathAll
	for folder in ${allFolders[@]};
	do
		if [[ -d "$folder" ]];then
		{
			for lib in ${glibomp[@]};
			do
				if [[ (-e "${folder}/${lib}") ]];then
                                        #arch=$(objdump -f ${folder}/${lib} | grep ^architecture)
                                        arch=$(file ${folder}/${lib}| grep $m32orm64key )
					if [[ $arch == *"$m32orm64key"* ]]; then
					    l_file="${folder}/${lib}"
					    flagPass_lib_path=1
					    break 2
					fi
				fi
			done
		}
		fi
	done
	IFS=$old_IFS

	if [[ ($flagPass_lib_path -eq 0) || ($flagPass_LD_lib_path -eq 0)  ]];then
		Fail_OpenMP $1
	else
		Pass_OpenMP "$l_file" $1
	fi
}

Fail_Flang_In_Path()
{
	gFailCount=$((gFailCount+1))
	gFailTestStatus="${gFailTestStatus}\t$gFailCount)WARNING: flang is not present in PATH\n\tWithout this compilation will fail for FORTRAN source code\n\tAdd bin folder of your AOCC compiler to PATH\n\n"
}

Fail_Flang1_In_Path()
{
        gFailCount=$((gFailCount+1))
        gFailTestStatus="${gFailTestStatus}\t$gFailCount)WARNING: flang1 is not present in PATH\n\tWithout this compilation will fail for FORTRAN source code\n\tAdd bin folder of your AOCC compiler to PATH\n\n"
}

Fail_Flang2_In_Path()
{
        gFailCount=$((gFailCount+1))
        gFailTestStatus="${gFailTestStatus}\t$gFailCount)WARNING: flang2 is not present in PATH\n\tWithout this compilation will fail for FORTRAN source code\n\tAdd bin folder of your AOCC compiler to PATH\n\n"
}

Pass_Flang_Test()
{
	gPassCount=$((gPassCount+1))
	tComp=$(dirname $gCompilerFlangUt )
	gPassTestStatus="${gPassTestStatus}\t$gPassCount) AOCC flang compiler bin => $tComp\n"
}

Test_Flang_In_Path()
{
	gCompilerFlangUt=$(which flang 2>/dev/null)
	if [[ ! (-e $gCompilerFlangUt) ]];then
		Fail_Flang_In_Path
	else
		 flng1=$(which flang1 2>/dev/null)
		 if [[ ! (-e $flng1) ]];then
                	Fail_Flang1_In_Path
		 else
			flng2=$(which flang2 2>/dev/null)
			if [[ ! (-e $flng2) ]];then
				Fail_Flang2_In_Path
			else
				Pass_Flang_Test
			fi
		 fi
	fi
}

Fail_Clang_In_Path()
{
	gFailCount=$((gFailCount+1))
	gFailTestStatus="${gFailTestStatus}\t$gFailCount)clang is not present in PATH\n\tWithout this compilation will fail for C source code\n\tAdd bin folder of your AOCC compiler to PATH\n\n"
}

Pass_ClangOrClang_Test()
{
	gPassCount=$((gPassCount+1))
	tComp=$(dirname $gCompilerUt)
	gPassTestStatus="${gPassTestStatus}\t$gPassCount) AOCC clang compiler bin => $tComp\n"
}

Test_Clang_In_Path()
{
	gCompilerUt=$(which clang 2>/dev/null)
	if [[ ! (-e $gCompilerUt) ]];then
		Fail_Clang_In_Path
	else
		Pass_ClangOrClang_Test
	fi
}

Fail_ClangPp_In_Path()
{
	gFailCount=$((gFailCount+1))
	gFailTestStatus="${gFailTestStatus}\t$gFailCount)clang++ is not present in PATH\n\tWithout this compilation will fail for C++ source code\n\tAdd bin folder of your AOCC compiler to PATH\n\n"
}

Pass_ClangOrClangPp_Test()
{
	gPassCount=$((gPassCount+1))
	tComp=$(dirname $gCompilerPpUt)
	gPassTestStatus="${gPassTestStatus}\t$gPassCount) AOCC clang++ compiler bin => $tComp\n"
}

Test_ClangPp_In_Path()
{
	gCompilerPpUt=$(which clang++ 2>/dev/null)
	if [[ ! (-e $gCompilerPpUt) ]];then
		Fail_ClangPp_In_Path
	else
		Pass_ClangOrClangPp_Test
	fi
}


Fail_libalm()
{
	gFailCount=$((gFailCount+1))
	gDynamicLibMissing=1
	gFailTestStatus="${gFailTestStatus}\t$gFailCount)WARNING:AMD LibM (libalm) is required in both LIBRARY_PATH and LD_LIBRARY_PATH\n\tMake sure that both LIBRARY_PATH and LD_LIBRARY_PATH have folder containing file ${glibalm[@]}\n\tYou can download AMD LibM from developer.amd.com \n\n"
}

Pass_libalm()
{
	gPassCount=$((gPassCount+1))
	gPassTestStatus="${gPassTestStatus}\t$gPassCount) AMD LibM (libalm) => $1\n"
}

Test_libalm()
{
local flagPass_LD_lib_path=0;
local flagPass_lib_path=0;
local l_file=""

	ldPathAll=$LD_LIBRARY_PATH
	old_IFS=$IFS
	IFS=':';allFolders=$ldPathAll
	for folder in ${allFolders[@]};
	do
		if [[ -d "$folder" ]];then
		{
			for lib in ${glibalm[@]};
			do
				if [[ (-e "${folder}/${lib}") ]];then
					l_file=${folder}/${lib}
					flagPass_LD_lib_path=1
					break 2
				fi
			done
		}
		fi
	done
	IFS=$old_IFS

	ldPathAll=$LIBRARY_PATH
	old_IFS=$IFS
	IFS=':';allFolders=$ldPathAll
	for folder in ${allFolders[@]};
	do
		if [[ -d "$folder" ]];then
		{
			for lib in ${glibalm[@]};
			do
				if [[ (-e "${folder}/${lib}") ]];then
					l_file=${folder}/${lib}
					flagPass_lib_path=1
					break 2
				fi
			done
		}
		fi
	done
	IFS=$old_IFS

	if [[ ($flagPass_lib_path -eq 0) || ($flagPass_LD_lib_path -eq 0)  ]];then
		Fail_libalm $1
	else
		Pass_libalm "$l_file" $1
	fi
}

Fail_libamdalloc()
{
        gFailCount=$((gFailCount+1))
        gDynamicLibMissing=1
        gFailTestStatus="${gFailTestStatus}\t$gFailCount)WARNING: AMD's memory allocation lib (libamdalloc) is required in both LIBRARY_PATH and LD_LIBRARY_PATH\n\tMake sure that both LIBRARY_PATH and LD_LIBRARY_PATH have folder containing file ${glibamdalloc[@]}\n\n"
}

Pass_libamdalloc()
{
        gPassCount=$((gPassCount+1))
        gPassTestStatus="${gPassTestStatus}\t$gPassCount) AMD's memory allocation lib (libamdalloc) => $1\n"
}

Test_libamdalloc()
{
local flagPass_LD_lib_path=0;
local flagPass_lib_path=0;
local l_file=""

        ldPathAll=$LD_LIBRARY_PATH
        old_IFS=$IFS
        IFS=':';allFolders=$ldPathAll
        for folder in ${allFolders[@]};
        do
                if [[ -d "$folder" ]];then
                {
                        for lib in ${glibamdalloc[@]};
                        do
                                if [[ (-e "${folder}/${lib}") ]];then
                                        l_file=${folder}/${lib}
                                        flagPass_LD_lib_path=1
                                        break 2
                                fi
                        done
                }
                fi
        done
        IFS=$old_IFS

        ldPathAll=$LIBRARY_PATH
        old_IFS=$IFS
        IFS=':';allFolders=$ldPathAll
        for folder in ${allFolders[@]};
        do
                if [[ -d "$folder" ]];then
                {
                        for lib in ${glibamdalloc[@]};
                        do
                                if [[ (-e "${folder}/${lib}") ]];then
                                        l_file=${folder}/${lib}
                                        flagPass_lib_path=1
                                        break 2
                                fi
                        done
                }
                fi
        done
        IFS=$old_IFS

        if [[ ($flagPass_lib_path -eq 0) || ($flagPass_LD_lib_path -eq 0)  ]];then
                Fail_libamdalloc
        else
                Pass_libamdalloc "$l_file"
        fi
}

Fail_Glibc_Version()
{
	gFailCount=$((gFailCount+1))
	gFailTestStatus="${gFailTestStatus}\t$gFailCount)Glibc version is less than ${gGlibcVersion}\n\tPlease upgrade to glibc version $gGlibcVersion or above\n\n"
}

Pass_Glibc_Version()
{
	gPassCount=$((gPassCount+1))
	gPassTestStatus="${gPassTestStatus}\t$gPassCount) Glibc version => $gCurrentGlibcVersion\n"
}

Test_Glibc_Version()
{
        glibcVersion=$(ldd --version)
	gCurrentGlibcVersion=$(echo $glibcVersion | awk -F' ' '{print $4}' | sed 's/(//g' | sed 's/)//g' )
        gCurrentGlibcVersion=$(echo $gCurrentGlibcVersion | sed 's/[ \t\n]//g')
	st=$(awk 'BEGIN{ print "'$gCurrentGlibcVersion'"<"'$gGlibcVersion'" }')
        if [[ $st -eq 1 ]]; then
          Fail_Glibc_Version
        else
          Pass_Glibc_Version
	fi
}

Fail_opt_viewer()
{
	gFailCount=$((gFailCount+1))
	gFailTestStatus="${gFailTestStatus}\t$gFailCount) In PATH opt-viewer is missing\n\t\n\n"
}

Pass_opt_viewer()
{
	gPassCount=$((gPassCount+1))
	gPassTestStatus="${gPassTestStatus}\t$gPassCount) In PATH opt-viewer is present\n"
}

Test_opt_viewer_In_Path()
{
 echo $PATH | grep -q "opt-viewer"
 ret_val=$?
 if [[ $ret_val -ne 0 ]]; then
	Fail_opt_viewer
 else
	Pass_opt_viewer
 fi
}

Fail_py_ver()
{
        gFailCount=$((gFailCount+1))
        gFailTestStatus="${gFailTestStatus}\t$gFailCount)WARNING: Python 3.5 or above is required for using OMPD, please refer to README for more details \n\n"
}

Pass_py_ver()
{
        gPassCount=$((gPassCount+1))
        gPassTestStatus="${gPassTestStatus}\t$gPassCount) Python 3.5 or above is present\n"
}

Test_py_ver()
{
       py_exe=$(which python3)
       if [[ -e $py_exe ]];then
		mv=$($py_exe --version | awk -F'.' '{print $2}')
		if [[ $mv -ge 5 ]];then
			Pass_py_ver
		else
			Fail_py_ver
		fi
       else
		Fail_py_ver
       fi
}


Usage()
{
	echo -e "\nThis script helps to check shell environment setting for AOCC usage"

        echo "Usage:"
        echo -e "\t $0 "
        echo -e "\t $0 -h for help\n"
}

Chk_Distribution()
{
         if [[ "$OSTYPE" == "linux"* ]]; then
           DISTRIB=$(awk -F= '/^NAME/{print $2}' /etc/os-release)
           DISTRIB_VER=$(awk -F= '/^VERSION_ID/{print $2}' /etc/os-release)

           DISTRIB=$(sed -e 's/^"//' -e 's/"$//' <<<"$DISTRIB" )
           DISTRIB_VER=$(sed -e 's/^"//' -e 's/"$//' <<<"$DISTRIB_VER" )

           if [[ "$DISTRIB" == "SLES" ]]; then
               if [[ $DISTRIB_VER == "15"* ]]; then
                  gFailCount=$((gFailCount+1))
                  gFailTestStatus="${gFailTestStatus}\t$gFailCount)WARNING: Building 32-Bit Applications in a 64-Bit SUSE System are not supported from SUSE Linux Enterprise Server 15 SP1.\n\tSo additional thirdparty package needs to be installed.\n\n"
               fi
           elif [[ "$DISTRIB" == "Red Hat Enterprise Linux"* ]]; then
               echo Running on OS $DISTRIB $DISTRIB_VER
           elif [[ "$DISTRIB" == "CentOS"* ]]; then
               echo Running on OS $DISTRIB $DISTRIB_VER
           elif [[ "$DISTRIB" == "Ubuntu"* ]]; then
               echo Running on OS "$DISTRIB $DISTRIB_VER"
           fi
         fi
}

check_m32_m64_lib_using_gcc_pkg()
{
        for lib_file in $1;
        do
        Selected_GCC_installation_dir=$(flang -E -### -v 2>&1 >/dev/null | grep "Selected GCC installation" | awk '{print $NF }') 

	if [ ! -d "$Selected_GCC_installation_dir" ]; then
            gFailCount=$((gFailCount+1))
	    gFailTestStatus="${gFailTestStatus}\t$gFailCount) WARNING: flang is not present in PATH, flang is used to check $2 Library $1 Location\n\n"
	    return
        fi

        if [ -e $Selected_GCC_installation_dir/$1 ]
        then
           lib_to_chk=$(readlink -f $Selected_GCC_installation_dir/$1)
           file_op=$(file $lib_to_chk)

           if [[ "$file_op" == *"$2"* ]]; then
              gPassCount=$((gPassCount+1))
              gPassTestStatus="${gPassTestStatus}\t$gPassCount) Check for $2 $lib_file library passed => $Selected_GCC_installation_dir/$1 \n"
           else
              #If the library is not resolved from the GCC installed path then try resolving the path through ldconfig tool
              check_m32_m64_lib $1 $2
           fi
        else
            #If the library is not resolved from the GCC installed path then try resolving the path through ldconfig tool
            check_m32_m64_lib $1 $2
        fi
        done

}

check_m32_m64_lib()
{
        for lib_file in $1;
        do
	lib_list=$(/sbin/ldconfig -p | grep -F $lib_file | awk '{print $NF }')
        ret1=$?


	File_available_status=0
	for lib_to_chk in $lib_list;
	do
	   file_op=$(file $lib_to_chk)
	   if [[ "$file_op" == *"symbolic link"* ]]; then
		   lib_to_chk=$(readlink -f $lib_to_chk)
		   file_op=$(file $lib_to_chk)
	   fi


	   if [[ "$file_op" == *"$2"* ]]; then
	       if [[ $File_available_status -eq 0 ]];then
                  gPassCount=$((gPassCount+1))
                  gPassTestStatus="${gPassTestStatus}\t$gPassCount) Check for $2 $lib_file library passed => $lib_to_chk \n"
		  File_available_status=1
                  break
               fi
	   fi
	done


        if [ $File_available_status -ne 1 ]; then
            gFailCount=$((gFailCount+1))
            gFailTestStatus="${gFailTestStatus}\t$gFailCount) $2 Library $1 is missing \n\n"
        fi
        done

}

Test_crt_files()
{
         if [[ "$OSTYPE" == "linux"* ]]; then
           DISTRIB=$(awk -F= '/^NAME/{print $2}' /etc/os-release)
           DISTRIB=$(sed -e 's/^"//' -e 's/"$//' <<<"$DISTRIB" )

           if [[ "$DISTRIB" == "Ubuntu"* ]]; then
                crt_32_bit_obj_path="/usr/lib32/"
                crt_64_bit_obj_path="/usr/lib/x86_64-linux-gnu/"
           else
                crt_32_bit_obj_path="/usr/lib/"
                crt_64_bit_obj_path="/usr/lib64/"
           fi
         fi

         for obj_file in crt1.o crti.o crtn.o;
         do
            if [[ "$1" == 32-bit ]]; then
               if [[ (-e "${crt_32_bit_obj_path}/${obj_file}") ]]; then
                     obj_file_exist="${obj_file_exist} ${obj_file}"
               else
                     obj_file_notexist="${obj_file_notexist} ${obj_file}"
		     obj_file_path=${crt_32_bit_obj_path}
               fi
            else
               if [[ (-e "${crt_64_bit_obj_path}/${obj_file}") ]]; then
                     obj_file_exist="${obj_file_exist} ${obj_file}"
               else
                     obj_file_notexist="${obj_file_notexist} ${obj_file}"
		     obj_file_path=${crt_64_bit_obj_path}
               fi
            fi
         done
         if [ ! -z "$obj_file_notexist" ];then
             gFailCount=$((gFailCount+1))
             gFailTestStatus="${gFailTestStatus}\t$gFailCount)WARNING: Folder containing $1 crt files $obj_file_notexist are not present in default path $obj_file_path \n\tWithout this $1 compilation will fail\n\n"
             obj_file_notexist=
         fi
         if [ ! -z "$obj_file_exist" ];then
             gPassCount=$((gPassCount+1))
	     gPassTestStatus="${gPassTestStatus}\t$gPassCount) Check for $1 crt files $obj_file_exist  passed\n"
             obj_file_exist=
         fi
}

gInput="$1"
if [[ ($1 == "-h") || ($1 == "--help")  ]];then
        Usage
else
	Test_Clang_In_Path
	Test_ClangPp_In_Path
	Test_Flang_In_Path
	Test_Glibc_Version
	Test_libalm
	Test_libamdalloc
	Test_py_ver
	Test_opt_viewer_In_Path
        check_m32_m64_lib libxml2.so "64-bit"
	Test_OpenMP 64-bit
	Test_OpenMP 32-bit
        check_m32_m64_lib_using_gcc_pkg libstdc++.so "64-bit"
        check_m32_m64_lib_using_gcc_pkg libstdc++.so "32-bit"
        check_m32_m64_lib "libc.so" "64-bit"
        check_m32_m64_lib "libc.so" "32-bit"
        check_m32_m64_lib "libm.so" "64-bit"
        check_m32_m64_lib "libm.so" "32-bit"
        check_m32_m64_lib libgcc_s.so "64-bit"
        check_m32_m64_lib libgcc_s.so "32-bit"
        check_m32_m64_lib libz.so "64-bit"
        check_m32_m64_lib libz.so "32-bit"
        check_m32_m64_lib_using_gcc_pkg libquadmath.so "64-bit"
        check_m32_m64_lib_using_gcc_pkg libquadmath.so "32-bit"
        Test_crt_files "64-bit"
        Chk_Distribution
        Test_crt_files "32-bit"
	printTestStatusAndExit
fi



