#!/bin/sh

#
# Copyright 2024 Signal Messenger, LLC
# SPDX-License-Identifier: AGPL-3.0-only
#

set -e

# shellcheck source=bin/env.sh
. "$(dirname "$0")"/env.sh

# Note: make sure to only use NodeJS architectures here, like x64, ia32, arm64, etc.
TARGET_ARCH=${TARGET_ARCH:-$(uname -m)}

usage()
{
    echo 'usage: build-desktop [-d|-r|-c] [--webrtc-only|--ringrtc-only|--call-sim-cli-only] [--no-electron] [--no-call-sim-cli] [--archive-webrtc] [--build-for-simulator] [--test-ringrtc-adm]
    where:
        -d to create a debug build (default)
        -r to create a release build
        -c to clean the build artifacts

        --webrtc-only builds libwebrtc.a only
        --ringrtc-only builds libringrtc-ARCH.node only
        --webrtc-tests also builds webrtc tests
        --test-ringrtc-adm also tests the ringrtc ADM.
        --build-for-simulator also builds extra test tools used in simulators

        --archive-webrtc generates an archive suitable for later --ringrtc-only builds'
}

clean()
{
    # Remove all possible artifact directories.
    rm -rf ./src/node/build
    rm -rf ./src/node/dist
    rm -rf ./src/node/node_modules
    cargo clean
}

BUILD_TYPE=debug
BUILD_WHAT=all
SKIP_CALL_SIM_CLI=
SKIP_ELECTRON=
ARCHIVE_WEBRTC=
TEST_RINGRTC_ADM=
BUILD_FOR_SIMULATOR=

while [ "$1" != "" ]; do
    case $1 in
        -d | --debug )
            BUILD_TYPE=debug
            ;;
        -r | --release )
            BUILD_TYPE=release
            ;;
        --webrtc-tests )
            BUILD_WEBRTC_TESTS=yes
            ;;
        --webrtc-only )
            BUILD_WHAT=webrtc
            ;;
        --ringrtc-only )
            BUILD_WHAT=ringrtc
            ;;
        --call-sim-cli-only )
            BUILD_WHAT=call_sim-cli
            ;;
        --no-electron )
            SKIP_ELECTRON=yes
            ;;
        --no-call-sim-cli )
            SKIP_CALL_SIM_CLI=yes
            ;;
        --archive-webrtc )
            ARCHIVE_WEBRTC=yes
            ;;
        --test-ringrtc-adm )
            TEST_RINGRTC_ADM=yes
            ;;
        --build-for-simulator )
            BUILD_FOR_SIMULATOR=yes
            ;;
        -c | --clean )
            clean
            exit
            ;;
        -h | --help )
            usage
            exit
            ;;
        * )
            usage
            exit 1
    esac
    shift
done

case "$TARGET_ARCH" in
    "x64"|"x86_64")
        GN_ARCH=x64
        CARGO_ARCH=x86_64
        # Normalize TARGET_ARCH to x64
        TARGET_ARCH=x64
        ;;
    "ia32")
        GN_ARCH=x86
        CARGO_ARCH=i686
        ;;
    "arm64"|"aarch64")
        GN_ARCH=arm64
        CARGO_ARCH=aarch64
        # Normalize TARGET_ARCH to arm64
        TARGET_ARCH=arm64
        ;;
    *)
        echo "Unsupported architecture"
        exit 1
        ;;
esac

export MACOSX_DEPLOYMENT_TARGET="10.15"

# Build WebRTC.
if [ "${BUILD_WHAT}" = "all" ] || [ "${BUILD_WHAT}" = "webrtc" ]
then
    echo "Building WebRTC for ${GN_ARCH}"

    WEBRTC_ARGS="target_cpu=\"${GN_ARCH}\" rtc_build_examples=false rtc_build_tools=false rtc_use_x11=false rtc_enable_sctp=false rtc_libvpx_build_vp9=true rtc_disable_metrics=true rtc_disable_trace_events=true use_siso=true"

    if [ -n "${BUILD_FOR_SIMULATOR}" ]
    then
      WEBRTC_ARGS="${WEBRTC_ARGS} rtc_use_dummy_audio_file_devices=true"
    fi

    if [ -n "${BUILD_WEBRTC_TESTS}" ]
    then
      WEBRTC_ARGS="${WEBRTC_ARGS} rtc_include_tests=true rtc_enable_protobuf=true"
    else
      WEBRTC_ARGS="${WEBRTC_ARGS} rtc_include_tests=false rtc_enable_protobuf=false"
    fi

    if [ "${BUILD_TYPE}" = "release" ]
    then
        WEBRTC_ARGS="${WEBRTC_ARGS} is_debug=false symbol_level=1"
    fi

    # SME requires the ARMv9-A architecture, while most ARM64 devices are ARMv8. This was causing
    # "undefined symbol: __arm_tpidr2_save" errors on such devices, including GitHub's ubuntu-22.04-arm runner.
    # https://issuetracker.google.com/issues/359006069
    # https://chromium.googlesource.com/libyuv/libyuv/+/61354d2671d9b5c73cc964415fe25bc76cea051a/BUILD.gn#273
    if [ "$(uname)" = "Linux" ] && [ "${TARGET_ARCH}" = "arm64" ]
    then
      WEBRTC_ARGS="${WEBRTC_ARGS} libyuv_use_sme=false"
    fi

    # Ensure that experimental compact relocation is disabled until upstream projects properly set it.
    # https://issues.webrtc.org/issues/407797634
    # https://chromium-review.googlesource.com/c/chromium/src/+/5938657
    if [ "$(uname)" = "Linux" ]
    then
      # Comment out the line that enables experimental crel.
      sed -i '/^[^#].*--allow-experimental-crel/ s/^/#/' src/webrtc/src/build/config/compiler/BUILD.gn
    fi

    (
        cd src/webrtc/src
        gn gen -C "out/${BUILD_TYPE}" "--args=${WEBRTC_ARGS}"
        third_party/siso/cipd/siso ninja -C "out/${BUILD_TYPE}" webrtc
        if [ -n "${BUILD_WEBRTC_TESTS}" ]
        then
          third_party/siso/cipd/siso ninja -C "out/${BUILD_TYPE}" default
          echo "Downloading test resources"
          download_from_google_storage --directory --recursive \
               --num_threads=10 --no_auth --quiet \
               --bucket chromium-webrtc-resources resources
        fi
        tools_webrtc/libs/generate_licenses.py --target :webrtc "out/${BUILD_TYPE}" "out/${BUILD_TYPE}"
    )

    mkdir -p "${OUTPUT_DIR}/${BUILD_TYPE}/obj"
    WEBRTC_OUT_PATH="src/webrtc/src/out"

    STATIC_LIB_PATH="${BUILD_TYPE}"/obj/webrtc.lib
    if [ ! -e "${WEBRTC_OUT_PATH}/${STATIC_LIB_PATH}" ]; then
      STATIC_LIB_PATH="${BUILD_TYPE}"/obj/libwebrtc.a
    fi
    cp -f "${WEBRTC_OUT_PATH}/${STATIC_LIB_PATH}" "${OUTPUT_DIR}/${BUILD_TYPE}/obj"
    cp -f "${WEBRTC_OUT_PATH}/${BUILD_TYPE}/LICENSE.md" "${OUTPUT_DIR}/${BUILD_TYPE}/"

    if [ -n "${ARCHIVE_WEBRTC}" ]
    then
        ARCHIVE_NAME=webrtc-"${WEBRTC_VERSION}"-"${HOST_PLATFORM}"-"${TARGET_ARCH}"-"${BUILD_TYPE}"
        if [ -n "${BUILD_FOR_SIMULATOR}" ]
        then
          ARCHIVE_NAME="${ARCHIVE_NAME}"-sim
        fi
        tar -c --auto-compress --dereference -f "${OUTPUT_DIR}"/"${ARCHIVE_NAME}".tar.bz2 -C "${OUTPUT_DIR}" "${STATIC_LIB_PATH}" "${BUILD_TYPE}/LICENSE.md"
    fi
fi

if [ -z "${SKIP_ELECTRON}" ] && [ "${BUILD_WHAT}" = "all" ] || [ "${BUILD_WHAT}" = "ringrtc" ]
then
    hash rustup 2>/dev/null || { echo >&2 "Make sure you have rustup installed and properly configured! Aborting."; exit 1; }

    RUSTFLAGS="${RUSTFLAGS:-}"

    case "$(rustup show active-toolchain)" in
        *"-apple-darwin"* )
            DEFAULT_PLATFORM="darwin"
            CARGO_TARGET="${CARGO_ARCH}-apple-darwin"
            # Set the version shown in crash dumps
            RUSTFLAGS="${RUSTFLAGS} -Clink-arg=-Wl,-current_version,${PROJECT_VERSION}"
            ;;
        *"-pc-windows"* )
            DEFAULT_PLATFORM="win32"
            CARGO_TARGET="${CARGO_ARCH}-pc-windows-msvc"
            # Static linking to prevent build errors on Windows ia32
            RUSTFLAGS="${RUSTFLAGS} -C target-feature=+crt-static"
            ;;
        *"-unknown-linux"* )
            DEFAULT_PLATFORM="linux"
            CARGO_TARGET="${CARGO_ARCH}-unknown-linux-gnu"
            ;;
        * )
            printf "Unknown platform detected!\nPlease make sure you have installed a valid Rust toolchain via rustup! Aborting.\n"
            exit 1
    esac

    echo "Building for platform ${DEFAULT_PLATFORM}, TARGET_ARCH=${TARGET_ARCH}, CARGO_TARGET=${CARGO_TARGET}, BUILD_FOR_SIMULATOR=${BUILD_FOR_SIMULATOR}"

    # Build and link the final RingRTC library.
    (
        INCLUDE_RELEASE_FLAG=
        if [ "${BUILD_TYPE}" = "release" ]
        then
            INCLUDE_RELEASE_FLAG=yes
        fi

        # Build with debug line tables, but not full debug info.
        export CARGO_PROFILE_RELEASE_DEBUG=1

        copy_to_node() {
            cp -f "$1" "$2"
        }

        if [ $DEFAULT_PLATFORM = "darwin" ]
        then
            # Save the debug info in dSYM format...
            export CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO=packed
            # ...then have Rust strip the library.
            export CARGO_PROFILE_RELEASE_STRIP=symbols
            OUTPUT_LIBRARY=libringrtc.dylib
            DEBUG_PACKAGE=libringrtc.dylib.dSYM
        elif [ $DEFAULT_PLATFORM = "win32" ]
        then
            # Save the debug info in PDB format...
            export CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO=packed
            # ...and DLLs don't have anything to strip.
            # (If you turn on stripping the PDB doesn't get generated at all.)
            OUTPUT_LIBRARY=ringrtc.dll
            DEBUG_PACKAGE=ringrtc.pdb
        elif [ $DEFAULT_PLATFORM = "linux" ]
        then
            # DWP files don't seem ready for everyday use.
            # We'll just save the whole unstripped binary.
            OUTPUT_LIBRARY=libringrtc.so
            DEBUG_PACKAGE=libringrtc.so
            # Manually strip on copy for release builds.
            if [ "${BUILD_TYPE}" = "release" ]; then
                OBJCOPY=$(command -v "${CARGO_ARCH}"-linux-gnu-objcopy || echo objcopy)
                copy_to_node() {
                    "${OBJCOPY}" -S "$1" "$2"
                }
            fi
        else
            echo 'should not get here; platforms checked above' >&2
            exit 1 # unreachable
        fi

        # Propagate this cross-compilation request to child processes;
        # e.g. cubeb rust libs.
        export CARGO_BUILD_TARGET="${CARGO_TARGET}"
        RUSTFLAGS="${RUSTFLAGS}" OUTPUT_DIR="${OUTPUT_DIR}" cargo rustc --package ringrtc --target ${CARGO_TARGET} --features electron ${INCLUDE_RELEASE_FLAG:+"--release"} --crate-type cdylib

        if [ "${TEST_RINGRTC_ADM}" = "yes" ]; then
          RUSTFLAGS="${RUSTFLAGS}" OUTPUT_DIR="${OUTPUT_DIR}" cargo test --package ringrtc --target ${CARGO_TARGET} --features electron ${INCLUDE_RELEASE_FLAG:+"--release"} -- --nocapture audio_device_module_tests
        fi


        mkdir -p src/node/build/${DEFAULT_PLATFORM}
        copy_to_node target/${CARGO_TARGET}/${BUILD_TYPE}/${OUTPUT_LIBRARY} src/node/build/${DEFAULT_PLATFORM}/libringrtc-"${TARGET_ARCH}".node

        if [ "${BUILD_TYPE}" = "release" ] && [ "$(command -v dump_syms)" ];
        then
          dump_syms "target/${CARGO_TARGET}/${BUILD_TYPE}/${DEBUG_PACKAGE}" -o "${OUTPUT_DIR}"/${BUILD_TYPE}/libringrtc-"${PROJECT_VERSION}"-${DEFAULT_PLATFORM}-"${TARGET_ARCH}"-debuginfo.sym
        fi
    )
fi

if [ -z "${SKIP_CALL_SIM_CLI}" ] && [ "${BUILD_WHAT}" = "all" ] || [ "${BUILD_WHAT}" = "call_sim-cli" ]
then
    if [ "${BUILD_TYPE}" = "debug" ]
    then
        OUTPUT_DIR="${OUTPUT_DIR}" cargo build --package ringrtc --bin call_sim-cli --features=call_sim
        echo "Can run with target/debug/call_sim-cli"
    else
        # Build with debug line tables, but not full debug info.
        CARGO_PROFILE_RELEASE_DEBUG=1 OUTPUT_DIR="${OUTPUT_DIR}" cargo build --package ringrtc --bin call_sim-cli --features=call_sim --release
        echo "Can run with target/release/call_sim-cli"
    fi
fi
