#!/bin/bash
# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------

set -e

BASEPATH=$(cd "$(dirname $0)"; pwd)
OUTPUT_PATH="${BASEPATH}/output"
BUILD_OUT_PATH="${BASEPATH}/build_out"
BUILD_RELATIVE_PATH="build"
RULE_LAUNCH_ARG=""

# MDC build para
ENABLE_BUILD_DEVICE=ON
USE_CXX11_ABI=0
CMAKE_TOOLCHAIN_FILE_VAL=""
CMAKE_TOOLCHAIN_PREFIX="${BASEPATH}/../cmake/toolchain"

# print usage message
usage() {
  echo "Usage:"
  echo "  sh build.sh [-h | --help] [-v | --verbose] [-j<N>]"
  echo "              [--output_path=<PATH>] [--cann_3rd_lib_path=<PATH>]"
  echo "              [--enable_symengine] [--build-type]"
  echo "              [--pkg-type=<TYPE>] [--asan] [--cov]"
  echo ""
  echo "Options:"
  echo "    -h, --help     Print usage"
  echo "    -v, --verbose  Show detailed build commands during the build process"
  echo "    -j<N>          Set the number of threads used for building Metadef, default is 8"
  echo "    --asan         Enable AddressSanitizer"
  echo "    --cov          Enable Coverage"
  echo "    --output_path=<PATH>"
  echo "                   Set output path, default ./output"
  echo "    --enable_symengine"
  echo "                   find symengine and boost"
  echo "    --build-type=<TYPE>"
  echo "                   Specify build type (TYPE option: Release/Debug), Default: Release"
  echo "    --pkg-type=<TYPE>"
  echo "                   Specify package type (TYPE option: run/rpm/deb), Default: run"
  echo "    --cann_3rd_lib_path=<PATH>"
  echo "                      Set third_party package install path, default ./output/third_party"
  echo "                      (Third_party package will cost a little time during the first compilation,"
  echo "                      it will skip compilation to save time during subsequent builds)"
  echo ""
}

parse_cmake_extra_args() {
    echo "Parse cmake extra args."
    # para check
    local args_str="$1"
    if [[ -z "$args_str" ]]; then
        echo "The parsed parameter string is empty."
        return 0
    fi

    IFS=',' read -ra kv_pairs <<< "$args_str"

    for kv_pair in "${kv_pairs[@]}"; do
        if [[ -z "$kv_pair" ]]; then
            continue
        fi

        local key="${kv_pair%%=*}"
        local value="${kv_pair#*=}"

        case "$key" in
            "ENABLE_BUILD_DEVICE")
                ENABLE_BUILD_DEVICE="$value"
                echo "Set ENABLE_BUILD_DEVICE to ${ENABLE_BUILD_DEVICE}."
                ;;
            "USE_CXX11_ABI")
                USE_CXX11_ABI="$value"
                echo "Set USE_CXX11_ABI to ${USE_CXX11_ABI}."
                ;;
            "CMAKE_TOOLCHAIN_FILE")
                CMAKE_TOOLCHAIN_FILE_VAL="${CMAKE_TOOLCHAIN_PREFIX}/${value}"
                export LLVM_PATH="${BASEPATH}/../build/bin/os/pegasus_llvm_libs/pegasus_llvm_x86_ubuntu_22_04_adk/hcc_arm64le_llvm/bin"
                echo "Set CMAKE_TOOLCHAIN_FILE_VAL to ${CMAKE_TOOLCHAIN_FILE_VAL}."
                ;;
            *)
                echo "invalid parameter key: $key"
                ;;
        esac
    done

    if [[ "X$(echo "$USE_CXX11_ABI" | tr '[:upper:]' '[:lower:]')" == "xon" || "$USE_CXX11_ABI" == "1" ]]; then
      USE_CXX11_ABI=1
    elif [[ "X$(echo "$USE_CXX11_ABI" | tr '[:upper:]' '[:lower:]')" == "xoff" || "$USE_CXX11_ABI" == "0" ]]; then
      USE_CXX11_ABI=0
    fi
}

# parse and set options
checkopts() {
  VERBOSE=""
  THREAD_NUM=$(grep -c ^processor /proc/cpuinfo)
  ENABLE_METADEF_UT="off"
  ENABLE_SYMENGINE="off"
  ENABLE_METADEF_ST="off"
  ENABLE_METADEF_COV="off"
  ENABLE_BENCHMARK="off"
  GE_ONLY="on"
  CANN_3RD_LIB_PATH="$BASEPATH/output/third_party"
  CMAKE_BUILD_TYPE="Release"
  PACKAGE_TYPE="run"

  # Process the options
  parsed_args=$(getopt -a -o j:hv -l help,verbose,enable_symengine,cann_3rd_lib_path:,extra-cmake-args:,build-type:,pkg-type:,asan,tsan,cov,rule_launch:,output_path: -- "$@") || {
    usage
    exit 1
  }

  eval set -- "$parsed_args"

  while true; do
    case "$1" in
      -h | --help)
        usage
        exit 0
        ;;
      -j)
        THREAD_NUM="$2"
        shift 2
        ;;
      -v | --verbose)
        VERBOSE="VERBOSE=1"
        shift
        ;;
      --cann_3rd_lib_path)
        CANN_3RD_LIB_PATH="$(realpath $2)"
        shift 2
        ;;
      --output_path)
        OUTPUT_PATH="$(realpath $2)"
        shift 2
        ;;
      --enable_symengine)
        ENABLE_SYMENGINE="on"
        shift
        ;;
      --build-type)
        if [ "X$2" != "XRelease" ] && [ "X$2" != "XDebug" ]; then
          usage && echo "Error: Invalid value '$2' for option '$1'" && exit 1
        fi
        CMAKE_BUILD_TYPE="$2"
        shift 2
        ;;
      --pkg-type)
        if [ "X$2" != "Xrun" ] && [ "X$2" != "Xrpm" ] && [ "X$2" != "Xdeb" ]; then
          usage && echo "Error: Invalid value '$2' for option '$1'" && exit 1
        fi
        PACKAGE_TYPE="$2"
        shift 2
        ;;
      --extra-cmake-args)
        parse_cmake_extra_args "$2"
        shift 2
        ;;
      --asan)
        ENABLE_ASAN="on"
        shift
        ;;
      --tsan)
        ENABLE_TSAN="on"
        shift
        ;;
      --cov)
        ENABLE_GCOV="on"
        shift
        ;;
      --rule_launch)
        RULE_LAUNCH_ARG="-D RULE_LAUNCH=$2"
        shift 2
        ;;
      --)
        shift
        if [ $# -ne 0 ]; then
          echo "ERROR: Undefined parameter detected: $*"
          usage
          exit 1
        fi
        break
        ;;
      *)
        echo "Undefined option: $1"
        usage
        exit 1
        ;;
    esac
  done

  if [ -n "$ASCEND_HOME_PATH" ]; then
    ASCEND_INSTALL_PATH="$ASCEND_HOME_PATH"
  else
    echo "Error: No environment variable 'ASCEND_HOME_PATH' was found, please check the cann environment configuration."
    exit 1
  fi
}

mk_dir() {
  local create_dir="$1"  # the target to make
  mkdir -pv "${create_dir}"
  echo "created ${create_dir}"
}

execute_command() {
  local cmd=$1
  ${cmd}
  if [ 0 -ne $? ]; then
    echo "Failed command: '${cmd}'."
    exit 1
  fi
}

copy_pkg() {
  if [ "${ENABLE_BUILD_DEVICE}" = "ON" ]; then
    mv ${BUILD_PATH}_CPack_Packages/makeself_staging/cann-*.run ${BUILD_OUT_PATH}/
  elif [[ "${CMAKE_TOOLCHAIN_FILE_VAL}" == *"ubuntu18.04-x86_64-llvm-toolchain.cmake"* ]]; then
    local aoskernel_file=$(basename "$(echo "$(ls ${BUILD_PATH}_CPack_Packages/makeself_staging/cann-*.run)" | sed 's/metadef_/metadef-/g; s/_linux-/-aoskernel./g')")
    mv ${BUILD_PATH}_CPack_Packages/makeself_staging/cann-*.run ${BUILD_OUT_PATH}/${aoskernel_file}
  else
    if [ -f "/etc/lsb-release" ]; then
      ubuntu_version=$(grep -E '^DISTRIB_RELEASE=' /etc/lsb-release | cut -d'=' -f2 | xargs)
      ubuntu_version="-ubuntu${ubuntu_version}."
      local runtime_file=$(basename "$(echo "$(ls ${BUILD_PATH}_CPack_Packages/makeself_staging/cann-*.run)" | sed "s/metadef_/metadef-/g; s/_linux-/${ubuntu_version}/g")")
      mv ${BUILD_PATH}_CPack_Packages/makeself_staging/cann-*.run ${BUILD_OUT_PATH}/${runtime_file}
    else
      echo "Error: operate environment is not ubuntu."
      exit 1
    fi
  fi
}

# create build path
build_metadef() {
  echo "create build directory and build Metadef"
  cd "${BASEPATH}"

  BUILD_PATH="${BASEPATH}/${BUILD_RELATIVE_PATH}/"
  CMAKE_ARGS="-D GE_ONLY=$GE_ONLY \
              -D ENABLE_OPEN_SRC=True \
              -D ENABLE_METADEF_UT=${ENABLE_METADEF_UT} \
              -D ENABLE_METADEF_ST=${ENABLE_METADEF_ST} \
              -D ENABLE_METADEF_COV=${ENABLE_METADEF_COV} \
              -D ENABLE_BENCHMARK=${ENABLE_BENCHMARK} \
              -D ENABLE_ASAN=${ENABLE_ASAN} \
              -D ENABLE_TSAN=${ENABLE_TSAN} \
              -D ENABLE_GCOV=${ENABLE_GCOV} \
              -D BUILD_WITHOUT_AIR=True \
              -D ASCEND_INSTALL_PATH=${ASCEND_INSTALL_PATH} \
              -D CANN_3RD_LIB_PATH=${CANN_3RD_LIB_PATH} \
              -D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \
              -D CMAKE_INSTALL_PREFIX=${OUTPUT_PATH} \
              -D ENABLE_SYMENGINE=${ENABLE_SYMENGINE} \
              -D ENABLE_BUILD_DEVICE=${ENABLE_BUILD_DEVICE} \
              -D USE_CXX11_ABI=${USE_CXX11_ABI} \
              -D LLVM_PATH=${LLVM_PATH} \
              -D CMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE_VAL} \
              -D PACKAGE_TYPE=${PACKAGE_TYPE} \
              ${RULE_LAUNCH_ARG}"

  echo "CMAKE_ARGS is: $CMAKE_ARGS"
  mk_dir "${BUILD_PATH}"
  cd ${BUILD_PATH}
  execute_command "cmake ${CMAKE_ARGS} .."
  execute_command "make all -j${THREAD_NUM} ${VERBOSE}"
  execute_command "make install"
  execute_command "make package"
  copy_pkg
  echo "Metadef build success!"
}

main() {
  cd "${BASEPATH}"
  checkopts "$@"

  g++ -v
  mk_dir ${OUTPUT_PATH}
  mk_dir ${BUILD_OUT_PATH}
  echo "---------------- Metadef build start ----------------"
  build_metadef || { echo "Metadef build failed."; exit 1; }
  echo "---------------- Metadef build finished ----------------"
}

main "$@"