#!/system/bin/sh
#
# Script to init the environment vor clang
#
# Usage: see below or execute the script with the parameter "--help" 
#
# Note:
#
# The script sources the file /data/local/tmp/local_init_clang20_env if it exists
#
# History
#  14.04.2025 v1.0.0 /bs
#    initial release
#

__TRUE=0
__FALSE=1

USER_DEFINED_INIT_SCRIPT="/data/local/tmp/local_init_clang20_env"

STANDALONE_SCRIPT=${__FALSE}

CONTINUE=${__TRUE}

PRINT_EXAMPLES=${__FALSE}

# ---------------------------------------------------------------------
# the variable TMPDIR is used by the /system/bin/sh to get the default directory for temporary files
#
export TMPDIR="${TMPDIR:=/data/local/tmp}"

# ---------------------------------------------------------------------

function LogMsg {
  echo "$*"
}

function LogWarning {
  [ $# -ne 0 ] && echo "WARNING: $*" >&2 || echo "" >&2
}

function LogError {
  [ $# -ne 0 ] && echo "ERROR: $*" >&2 || echo "" >&2
}

function LogInfo {
  if [ "${VERBOSE}"x != ""x ] ; then
    [ $# -ne 0 ] && echo "INFO: $*" >&2 || echo "" >&2
  fi
}

function LogDebug {
  if [ "${DEBUG}"x != ""x ] ; then
    [ $# -ne 0 ] && echo "DEBUG: $*" >&2 || echo "" >&2
  fi
}


# ---------------------------------------------------------------------
# add_string - add a string to the value of an environment variable if it is not yet part of the value
#
# Usage: add_string {environment_variable} {new_string} [separator] [c|b|e]
#
# Default separator is a blank if there is at least one blank in the existing value. 
# In all other cases the default separator is the colon (:).
#
# The known values for the 4th parameter are:
#   c - check only: do NOT change valiue of the variable
#   b - add the new value before the existing value
#   e - add the new value after the existing value (this is the default)
# 
function add_string {

  if [ "${DEBUG}"x != ""x ] ; then
    LogDebug "Executing \"$0 $*\" "
    set -x
  fi
  
  typeset ENV_VAR=""
  typeset NEW_VALUE=""
  typeset SEPARATOR="" 
  typeset POSITION="e"

  typeset CUR_VALUE=""

  typeset THISRC=${__TRUE}


  if [ $# -ge 2 -a $# -le 4 ] ; then
    ENV_VAR="$1"
    NEW_VALUE="$2"
    
    eval CUR_VALUE="\"\$${ENV_VAR}\""

    if [ $# -eq 4 ] ; then

      case $4 in 
      
        c | check )
          POSITION=""
          ;;

        e )
          POSITION="e"
          ;;
        b )
          POSITION="b"
          ;;

        * )
          LogError "$0 - unknown value for the action parameter: \"$4\" " 
          THISRC=${__FALSE}
          ;;
      esac
    fi
    
    if [ ${THISRC} = ${__TRUE} ] ; then
      if [ $# -eq 3 ] ; then
        SEPARATOR="$3"
      else
        echo "${CUR_VALUE}" | grep " " >/dev/null && SEPARATOR=" " || SEPARATOR=":"
      fi
    
      echo "${SEPARATOR}${CUR_VALUE}${SEPARATOR}"  | grep -- "${SEPARATOR}${NEW_VALUE}${SEPARATOR}" >/dev/null    
      if [ $? -ne 0 ] ; then
        case  ${POSITION} in
          e )
            eval export "${ENV_VAR}=\"\${CUR_VALUE}${SEPARATOR}${NEW_VALUE}\""
            ;;
          b )
            eval export "${ENV_VAR}=\"${NEW_VALUE}${SEPARATOR}\${CUR_VALUE}\""
            ;;
        esac
                          
        THISRC=${__TRUE}
      fi
    fi
  else
    LogError "$0 - wrong number of parameter: \"$*\" "
    THISRC=${__FALSE}
  fi
  
  return ${THISRC}
}


# ---------------------------------------------------------------------


# for testing only
#
while false ; do
  printf ">> "
  read USER_INPUT
  set -x
  eval ${USER_INPUT}
  set +x
done

# ---------------------------------------------------------------------

if [ ${CONTINUE} = ${__TRUE} ] ; then

  while [ $# -ne 0 ] ; do
    CUR_PARAMETER="$1"
    shift
    
    case ${CUR_PARAMETER} in
 
      -h | --help | help )
        cat <<EOT
        
Usage: . $0 [-h|--help] [init] [no_user] [var=value] [examples]
 
init        - clear the used enviroment variables at script start
no_user     - do not execute ${USER_DEFINED_INIT_SCRIPT}
var=value   - set the environment variable "var" to "value" at start of the script
examples    - print compile examples 

EOT

        CONTINUE=${__FALSE}
        ;;

      no_user )
        LogMsg "Executing the user defined init script is disabled now."
        USER_DEFINED_INIT_SCRIPT=""
        ;;
         
      examples | example | ex )
        PRINT_EXAMPLES=${__TRUE}
        ;;

      init | clear )
        LogMsg "Clearing all used variables ..."
        unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS  API SYSROOT TARGET_ROOT CPU_TYPE  NDK_DIR
        ;;
    
      *=* )
        eval ${CUR_PARAMETER}
        if [ $? -ne 0 ] ; then
          LogError "Error executing \"${CUR_PARAMETER}\" "
          CONTINUE=${__FALSE}
        fi
        ;;

      * )
        LogError "Unknown parameter found: \"${CUR_PARAMETER}\" "
        CONTINUE=${__FALSE}
        ;;
    esac
  done

  if [[ $0 == *init_clang*_env* ]] ; then
    MESSAGE="WARNING: The script $0 must be sourced in"
    n="${#MESSAGE}"
    (( n = n + 2 )) 
    THIS_MESSAGE="$( 
  printf "\n" 
  printf '%*s\n' "$n" '' | tr ' ' '*'
  printf " ${MESSAGE} \n"
  printf '%*s\n' "$n" '' | tr ' ' '*'
  printf "\n" 
                  )"

    LogMsg "${THIS_MESSAGE}"
    
    STANDALONE_SCRIPT=${__TRUE}                 
  fi


 
# ---------------------------------------------------------------------

  CLANG_VERSION="${CLANG_VERSION:=20}"

  NDK_VERSION="${NDK_VERSION:=r29-beta1}"

  API="${API:=31}"
  

  CPU_TYPE="${CPU_TYPE:=$( uname -m )}"

  TARGET_ROOT="${TARGET_ROOT:=/data/local/tmp/develop/sysroot}"


  CLANG_DIR="${CLANG_DIR:=/data/local/tmp/sysroot/usr/clang${CLANG_VERSION}}"
 
  NDK_DIR="${NDK_DIR:=/data/local/tmp/sysroot/usr/ndk/${NDK_VERSION}}"

  SYSROOT="${SYSROOT:=${NDK_DIR}/sysroot}"

  LIB_DIRS="${LIB_DIRS} ${NDK_DIR}/lib ${NDK_DIR}/lib/${CPU_TYPE}-unknown-linux-musl"
   
  LD_LIBRARY_PATH_DIRS="${SYSROOT}/usr/lib/${CPU_TYPE}-linux-android ${CLANG_DIR}/lib"

  OBJ_DIRS=""
  
  INCLUDE_DIRS="${CLANG_DIR}/include"

  BIN_DIRS="${CLANG_DIR}/bin"

  SYSROOT_CONFIGURE_OPTION=""
 
  if [ "${NDK_DIR}"x != ""x ] ; then
    if [ -d  "${NDK_DIR}" ] ; then
      INCLUDE_DIRS="${INCLUDE_DIRS} ${NDK_DIR}/include"
    fi
  fi

  if [ "${SYSROOT}"x != ""x ] ; then
    if [ -d  "${SYSROOT}" ] ; then

      SYSROOT_LIB_DIR="${SYSROOT}/usr/lib/${CPU_TYPE}-linux-android"

      INCLUDE_DIRS="${INCLUDE_DIRS} ${SYSROOT}/usr/include"

      if [ -d ${SYSROOT_LIB_DIR}/${API} ] ; then       
        LIB_DIRS="${LIB_DIRS} ${SYSROOT_LIB_DIR}/${API}"
        OBJ_DIRS="${OBJ_DIRS} ${SYSROOT_LIB_DIR}/${API}"
      fi

      if [ -d ${SYSROOT_LIB_DIR}/ ] ; then
        LIB_DIRS="${LIB_DIRS} ${SYSROOT_LIB_DIR}"
      fi
      
      [ -d ${SYSROOT}/usr/bin ] && BIN_DIRS="${BIN_DIRS} ${SYSROOT}/usr/bin"

      SYSROOT_CONFIGURE_OPTION="--sysroot ${SYSROOT}"

    else
      LogWarning "The sysroot directory \"${SYSROOT}\" does not exist"	  
    fi
  fi

  CLANG="${CLANG_DIR}/bin/clang"
  
  add_string PATH "${CLANG_DIR}/bin" ":" "b"
  
# ---------------------------------------------------------------------

  if [ ! -x "${CLANG}" ] ; then
    LogError "The binary \"${CLANG}\" does not exist or is not executable"
    CONTINUE=${__FALSE}
  fi
fi

# ---------------------------------------------------------------------

if [ ${CONTINUE} = ${__TRUE} ] ; then
 
  LogMsg
  LogMsg "Preparing the clang environment for creating binaries for the CPU type ${CPU_TYPE}"

  if [ "${TARGET_ROOT}"x != ""x ] ; then
    if [ -d  "${TARGET_ROOT}" ] ; then
      INCLUDE_DIRS="${INCLUDE_DIRS} ${TARGET_ROOT}/usr/include"
      BIN_DIRS="${BIN_DIRS} ${TARGET_ROOT}/usr/bin"
      LIB_DIRS="${LIB_DIRS} ${TARGET_ROOT}/usr/lib"
    else
      LogMsg "INFO: The target root directory \"${TARGET_ROOT}\" does not exist"  
    fi
  fi
  
  LogMsg""
  
  LogMsg "$( printf "%-90s %s\n" "Using clang ${CLANG_VERSION}" )"
  

  if [ "${NDK_DIR}"x != ""x ] ; then
    LogMsg "$( printf "%-90s %s" "Using the NDK ${NDK_DIR}" "(environment variable NDK_DIR)" )"
  fi

  if [ "${SYSROOT}"x != ""x ] ; then
    LogMsg "$( printf "%-90s %s" "Using the sysroot directory ${SYSROOT}" "(environment variable SYSROOT)" )"    
  fi

  if [ "${TARGET_ROOT}"x != ""x ] ; then
    LogMsg "$( printf "%-90s %s" "Using the target root directory ${TARGET_ROOT}" "(environment variable TARGET_ROOT)" )"
  fi

  LogMsg
  
  for CUR_DIR in ${BIN_DIRS} ; do
    echo ":${PATH}:" | grep ":${CUR_DIR}:" >/dev/null
    if [ $? -ne 0 ] ; then
      if [ ! -d "${CUR_DIR}" ] ; then
        LogWarning "The directory \"${CUR_DIR}\" does not exist"
      fi
      add_string PATH "${CUR_DIR}" ":"
    fi
  done
 
 
  for CUR_DIR in ${INCLUDE_DIRS} ; do
    if [ ! -d "${CUR_DIR}" ] ; then
      LogWarning "The directory \"${CUR_DIR}\" does not exist"
    fi

    add_string CFLAGS "-I${CUR_DIR}" " "
    add_string CPPFLAGS "-I${CUR_DIR}" " "
    add_string CXXFLAGS "-I${CUR_DIR}" " "
  
  done

  for CUR_DIR in ${LIB_DIRS} ; do
    if [ ! -d "${CUR_DIR}" ] ; then
      LogWarning "The directory \"${CUR_DIR}\" does not exist"
    fi

    add_string LDFLAGS "-L${CUR_DIR}" " "
  
  done

  for CUR_DIR in ${OBJ_DIRS} ; do
    if [ ! -d "${CUR_DIR}" ] ; then
      LogWarning "The directory \"${CUR_DIR}\" does not exist"
    fi
  
    add_string LDFLAGS "-B${CUR_DIR}" " "
  done

  
  for CUR_DIR in ${LD_LIBRARY_PATH_DIRS} ; do
    if [ ! -d "${CUR_DIR}" ] ; then
      LogWarning "The directory \"${CUR_DIR}\" does not exist"
    fi

    add_string LD_LIBRARY_PATH "${CUR_DIR}" ":"
  done

  export LD_LIBRARY_PATH

  if [ "${SYSROOT_CONFIGURE_OPTION}"x != ""x ] ; then 
    add_string CFLAGS "--sysroot=${SYSROOT}" " "
    add_string CPPLAGS "--sysroot=${SYSROOT}" " "
    add_string CXXLAGS "--sysroot=${SYSROOT}" " "
    add_string LDFLAGS "--sysroot=${SYSROOT}" " "
  fi

  [[ " ${LDFLAGS} " != *\ -lc\ * ]] && LDFLAGS="${LDFLAGS} -lc  "
  export LDFLAGS


#  add_string CFLAGS "-D__ANDROID_API__=${API}" " "
#  add_string CXXLAGS "-D__ANDROID_API__=${API}" " "
#  add_string CPPFLAGS "-D__ANDROID_API__=${API}" " "

  add_string CXXFLAGS "--target=${CPU_TYPE}-linux-android${API}" " "
 
  export PATH CFLAGS CXXFLAGS CPPFLAGS LDFLAGS SYSROOT TARGET_ROOT API NDK SYSROOT

  export CC="${CLANG_DIR}/bin/clang"
  export CXX="${CLANG_DIR}/bin/clang++"
  export AR="${CLANG_DIR}/bin/llvm-ar"
  export AS="${CLANG_DIR}/bin/llvm-as"
  export LD="${CLANG_DIR}/bin/lld"
  export RANLIB="${CLANG_DIR}/bin/llvm-ranlib"
  export STRIP="${CLANG_DIR}/bin/llvm-strip"
  export CPP="${CLANG_DIR}/bin/clang-cpp"
  export CXXCPP="${CLANG_DIR}/bin/clang-cpp"
  export PKG_CONFIG="${CLANG_DIR}/bin/pkg-config"

  LogMsg "Environment variables used:"
  
  LogMsg "$(
  printf "\n"
  printf "%-20s %s\n" "PATH is now:"             "${PATH}"
  printf "\n"
  
  printf "%-20s %s\n" "LD_LIBRARY_PATH:"         "${LD_LIBRARY_PATH}"
  printf "\n"
  printf "%-20s %s\n" "API is now:"              "${API}"
  printf "%-20s %s\n" "NDK_DIR is now:"          "${NDK_DIR}"
  printf "\n"
  printf "%-20s %s\n" "SYSROOT is now:"          "${SYSROOT}"
  printf "\n"

  printf "%-20s %s\n" "CFLAGS are now:"          "${CFLAGS}"
  printf "\n"
  printf "%-20s %s\n" "CPPFLAGS are now:"        "${CPPFLAGS}"
  printf "\n"
  printf "%-20s %s\n" "CXXFLAGS are now:"        "${CXXFLAGS}"
  printf "\n"
  printf "%-20s %s\n" "LDFLAGS are now:"         "${LDFLAGS}"
  printf "\n\n"
         )"

  LogMsg 
  for CUR_VAR in CC CXX CPP CXXCPP AR AS LD RANLIB STRIP PKG_CONFIG ; do
    eval CUR_VALUE="\$$CUR_VAR"
    LogMsg "$( printf "%-20s %s" "${CUR_VAR} is now:" "${CUR_VALUE}" )"
  done
 
  LogMsg 

  if [ ! -d /tmp ] ; then
    if [ "${TMP}"x = ""x ] ; then
      if [ -d /data/local/tmp ] ; then
        export TMP="/data/local/tmp"
        LogMsg "$( printf "%-20s %s" "TMP is now:"         "${TMP}")"
      else
        LogWarning "The directories /tmp and /data/local/tmp do not exist - please set the variable TMP before using clang"
      fi
    fi
  fi 

  LogMsg "Checking the clang binary ..."
  LogMsg
  
  ${CLANG} --version
  THISRC=$?

  LogMsg
  
  if [ ${THISRC} != 0 ] ; then
    LogWarning "Executing \"${CLANG} --version\" ended with RC=${THISRC}"
  fi
  
  if [ $API -lt 30 ] ; then
    LogMsg
    LogWarning "Comipling C++ programs using API version less then 30 will probably not work"
    LogMsg
  fi

  export CC=${CLANG}
  export LD=${LLD}

  if [ "${USER_DEFINED_INIT_SCRIPT}"x != ""x ] ; then
    if [ -r "${USER_DEFINED_INIT_SCRIPT}" ] ; then
      LogMsg "
${USER_DEFINED_INIT_SCRIPT} found -- now executing that file ...
"

      source "${USER_DEFINED_INIT_SCRIPT}"
      
      LogMsg "
... ${USER_DEFINED_INIT_SCRIPT} executed
"

    fi
  fi
fi

if [ ${PRINT_EXAMPLES} = ${__TRUE} ] ; then

  LogMsg "

To test the clang environment use

cd /data/local/tmp/sysroot/usr/share/clang20/
clang \${CFLAGS} \${LDFLAGS} -o helloworld_in_c helloworld_in_c.c  && ./helloworld_in_c

To test the C++ compiler use

cd /data/local/tmp/sysroot/usr/share/clang20/
clang++ \${CXXFLAGS} -lc++_shared \${LDFLAGS}  -o helloworld_in_c++ ./helloworld_in_c++.cpp && ./helloworld_in_c++

"

fi
