#!/bin/sh

# Copyright (c) 2021-2024 刘富频
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


# last line mode :set foldmethod=marker
# command mode zM  fold close all
# command mode zR  fold open all

# {{{ coding convention

# 1. The variable/function name starts with underscore "_" means that it is a private variable/function.
# 2. 0 represents the boolean value false
# 3. 1 represents the boolean value true

# }}}
##############################################################################
# {{{ utils

COLOR_RED='\033[0;31m'          # Red
COLOR_GREEN='\033[0;32m'        # Green
COLOR_YELLOW='\033[0;33m'       # Yellow
COLOR_BLUE='\033[0;94m'         # Blue
COLOR_PURPLE='\033[0;35m'       # Purple
COLOR_OFF='\033[0m'             # Reset

print() {
    printf '%b' "$*"
}

echo() {
    printf '%b\n' "$*"
}

note() {
    printf '%b\n' "${COLOR_YELLOW}🔔  $*${COLOR_OFF}" >&2
}

warn() {
    printf '%b\n' "${COLOR_YELLOW}⚠️  $*${COLOR_OFF}" >&2
}

success() {
    printf '%b\n' "${COLOR_GREEN}✅️  $*${COLOR_OFF}" >&2
}

error() {
    printf '%b\n' "${COLOR_RED}💔  ndk-pkg: $*${COLOR_OFF}" >&2
}

abort() {
    EXIT_STATUS_CODE="$1"
    shift
    printf '%b\n' "${COLOR_RED}💔  ndk-pkg: $*${COLOR_OFF}" >&2
    exit "$EXIT_STATUS_CODE"
}

step() {
    STEP_NUM=$(expr ${STEP_NUM-0} + 1)
    STEP_MESSAGE="$*"
    printf '\n%b\n' "${COLOR_PURPLE}=>> STEP ${STEP_NUM} : ${STEP_MESSAGE} ${COLOR_OFF}"
}

run() {
    echo "${COLOR_PURPLE}==>${COLOR_OFF} ${COLOR_GREEN}$@${COLOR_OFF}"
    eval "$@"
}

list_size() {
    printf '%s\n' "$#"
}

isInteger() {
    case "${1#[+-]}" in
        (*[!0123456789]*) return 1 ;;
        ('')              return 1 ;;
        (*)               return 0 ;;
    esac
}

bppend_to_PATH() {
    case ":${PATH}:" in
        *:"$1":*) ;;
        *) export PATH="$1:$PATH" ;;
    esac
}

bppend_to_ACLOCAL_PATH() {
    case ":${ACLOCAL_PATH}:" in
        *:"$1":*) ;;
        *) export ACLOCAL_PATH="$1:$ACLOCAL_PATH" ;;
    esac
}

git() {
    if [ -z "$SSL_CERT_FILE" ] ; then
        command git "$@"
    else
        command git -c http.sslCAInfo="$SSL_CERT_FILE" "$@"
    fi
}

# git_checkout <URL> --ref-from=<FROM> --ref-to=<TO> --depth=<N> -B <CHECKOUT-BRANCH-NAME> -C <WORKDIR> --recursive
git_checkout() {
    OLDCWD="$PWD"

    unset GIT_FETCH_FROM_URL
    unset GIT_FETCH_FROM_REF
    unset GIT_FETCH_TO___REF
    unset GIT_FETCH_DEPTH
    unset GIT_FETCH_SUBMODULE_RECURSIVE
    unset GIT_CHECKOUT_BRANCH_NAME
    unset GIT_WORK_DIR

    if [ -z "$NDKPKG_URL_TRANSFORM" ] ; then
        GIT_FETCH_FROM_URL="$1"
    else
        GIT_FETCH_FROM_URL="$("$NDKPKG_URL_TRANSFORM" "$1")"
    fi

    shift

    while [ -n "$1" ]
    do
        case $1 in
            --ref-from=*)
                GIT_FETCH_FROM_REF="${1#*=}"
                ;;
            --ref-to=*)
                GIT_FETCH_TO___REF="${1#*=}"
                ;;
            --depth=*)
                GIT_FETCH_DEPTH="${1#*=}"
                ;;
            -B) shift
                GIT_CHECKOUT_BRANCH_NAME="${1#*=}"
                ;;
            -C) shift
                GIT_WORK_DIR="$1"
                ;;
            --recursive)
                GIT_FETCH_SUBMODULE_RECURSIVE=1
                ;;
        esac
        shift
    done

    [ -z "$GIT_FETCH_DEPTH" ] && GIT_FETCH_DEPTH=1
    [ -z "$GIT_FETCH_FROM_REF" ] && GIT_FETCH_FROM_REF='HEAD'
    [ -z "$GIT_FETCH_TO___REF" ] && GIT_FETCH_TO___REF='refs/remotes/origin/master'

    [ -z "$GIT_CHECKOUT_BRANCH_NAME" ] && GIT_CHECKOUT_BRANCH_NAME="${GIT_FETCH_TO___REF##*/}"

    if [    -n "$GIT_WORK_DIR" ] ; then
        [   -d "$GIT_WORK_DIR" ] || run install -d "$GIT_WORK_DIR"
        run cd "$GIT_WORK_DIR"
    fi

    run git -c init.defaultBranch=master init
    run git remote add origin "$GIT_FETCH_FROM_URL"
    run git -c protocol.version=2 fetch --progress --depth="$GIT_FETCH_DEPTH" origin "$GIT_FETCH_FROM_REF:$GIT_FETCH_TO___REF"
    run git checkout --progress --force -B "$GIT_CHECKOUT_BRANCH_NAME" "$GIT_FETCH_TO___REF"

    git_submodule_update_recursive

    run cd "$OLDCWD"
}

git_submodule_update_recursive() {
    if [ -z "$1" ] ; then
        GIT_REPO_ROOT_DIR="$PWD"
    else
        GIT_REPO_ROOT_DIR="$1"
    fi

    GIT_SUBMODULE_HAVE="$(cd "$GIT_REPO_ROOT_DIR" && find . -type f -name '.gitmodules' -print -quit)"

    if [ -n "$GIT_SUBMODULE_HAVE" ] ; then
        if [ -z "$NDKPKG_URL_TRANSFORM" ] ; then
            run git submodule update --init --recursive
        else
            unset GIT_SUBMODULE_BASEDIR_STACK

            GIT_SUBMODULE_CONFIG_FILE_LIST="$(find "$GIT_REPO_ROOT_DIR" -type f -name '.gitmodules')"

            for f in $GIT_SUBMODULE_CONFIG_FILE_LIST
            do
                if [ -z "$GIT_SUBMODULE_BASEDIR_STACK" ] ; then
                    GIT_SUBMODULE_BASEDIR_STACK="${f%/*}"
                else
                    GIT_SUBMODULE_BASEDIR_STACK="$GIT_SUBMODULE_BASEDIR_STACK;${f%/*}"
                fi
            done

            while [ -n "$GIT_SUBMODULE_BASEDIR_STACK" ]
            do
                case $GIT_SUBMODULE_BASEDIR_STACK in
                    *\;*) GIT_SUBMODULE_BASEDIR="${GIT_SUBMODULE_BASEDIR_STACK##*;}" ; GIT_SUBMODULE_BASEDIR_STACK="${GIT_SUBMODULE_BASEDIR_STACK%;*}" ;;
                    *)    GIT_SUBMODULE_BASEDIR="${GIT_SUBMODULE_BASEDIR_STACK}"     ; GIT_SUBMODULE_BASEDIR_STACK=
                esac

                run cd "$GIT_SUBMODULE_BASEDIR"

                GIT_SUBMODULE_NAME_LIST="$(sed -n '/\[submodule ".*"\]/p' .gitmodules | sed 's|\[submodule "\(.*\)"\]|\1|')"

                for GIT_SUBMODULE_NAME in $GIT_SUBMODULE_NAME_LIST
                do
                    GIT_SUBMODULE_PATH="$(git config --file=.gitmodules --get "submodule.$GIT_SUBMODULE_NAME.path")"
                    GIT_SUBMODULE_URL="$(git config --file=.gitmodules --get "submodule.$GIT_SUBMODULE_NAME.url")"
                    GIT_SUBMODULE_URI="$("$NDKPKG_URL_TRANSFORM" "$GIT_SUBMODULE_URL")"

                    run git submodule set-url "$GIT_SUBMODULE_PATH" "$GIT_SUBMODULE_URI"
                done

                run git submodule update --init

                GIT_SUBMODULE_PATH_LIST="$(git submodule | sed 's|^ *||' | cut -d ' ' -f2)"

                for GIT_SUBMODULE_PATH in $GIT_SUBMODULE_PATH_LIST
                do
                    GIT_SUBMODULE_CONFIG_FILE_LIST="$(find "$GIT_SUBMODULE_PATH" -type f -name '.gitmodules')"

                    for f in $GIT_SUBMODULE_CONFIG_FILE_LIST
                    do
                        if [ -z "$GIT_SUBMODULE_BASEDIR_STACK" ] ; then
                            GIT_SUBMODULE_BASEDIR_STACK="$GIT_SUBMODULE_BASEDIR/${f%/*}"
                        else
                            GIT_SUBMODULE_BASEDIR_STACK="$GIT_SUBMODULE_BASEDIR_STACK;$GIT_SUBMODULE_BASEDIR/${f%/*}"
                        fi
                    done
                done
            done
        fi
    fi
}

# }}}
##############################################################################
# {{{ wfetch

# wfetch <URL> [--uri=<URL-MIRROR>] [--sha256=<SHA256>] [-o <OUTPUT-PATH>] [-q] [--no-buffer]
#
# If -o <OUTPUT-PATH> option is unspecified, the result will be written to <OUTPUT-PATH>/$(basename <URL>)
#
# If <OUTPUT-PATH> is -, then the result will be written to stdout.
#
# If <OUTPUT-PATH> is . .. or ends with slash(/), then it will be treated as a directory, otherwise, it will be treated as a filepath.
#
# If <OUTPUT-PATH> is treated as a directory, then it will be expanded to <OUTPUT-PATH>/$(basename <URL>)
#
# influential environment variable:
# NDKPKG_URL_TRANSFORM
wfetch() {
    unset FETCH_UTS
    unset FETCH_SHA

    unset FETCH_URL
    unset FETCH_URI

    unset FETCH_PATH

    unset FETCH_OUTPUT_DIR
    unset FETCH_OUTPUT_FILEPATH
    unset FETCH_OUTPUT_FILENAME

    unset FETCH_BUFFER_FILEPATH

    unset FETCH_SHA256_EXPECTED

    unset FETCH_SILENT

    unset NOT_BUFFER

    [ -z "$1" ] && abort 1 "wfetch <URL> [OPTION]... , <URL> must be non-empty."

    if [ -z "$NDKPKG_URL_TRANSFORM" ] ; then
        FETCH_URL="$1"
    else
        FETCH_URL="$("$NDKPKG_URL_TRANSFORM" "$1")" || return 1
    fi

    shift

    while [ -n "$1" ]
    do
        case $1 in
            --uri=*)
                FETCH_URI="${1#*=}"
                ;;
            --sha256=*)
                FETCH_SHA256_EXPECTED="${1#*=}"
                ;;
            -o) shift
                if [ -z "$1" ] ; then
                    abort 1 "wfetch <URL> -o <PATH> , <PATH> must be non-empty."
                else
                    FETCH_PATH="$1"
                fi
                ;;
            -q)
                FETCH_SILENT=1
                ;;
            --no-buffer)
                NOT_BUFFER=1
                ;;
            *)  abort 1 "wfetch <URL> [--uri=<URL-MIRROR>] [--sha256=<SHA256>] [-o <PATH>] [-q] , unrecognized option: $1"
        esac
        shift
    done

    if [ -z "$FETCH_URI" ] ; then
        # remove query params
        FETCH_URI="${FETCH_URL%%'?'*}"
        FETCH_URI="https://fossies.org/linux/misc/${FETCH_URI##*/}"
    else
        if [ -n "$NDKPKG_URL_TRANSFORM" ] ; then
            FETCH_URI="$("$NDKPKG_URL_TRANSFORM" "$FETCH_URI")" || return 1
        fi
    fi

    case $FETCH_PATH in
        -)
            FETCH_BUFFER_FILEPATH='-'
            ;;
        .|'')
            FETCH_OUTPUT_DIR='.'
            FETCH_OUTPUT_FILEPATH="$FETCH_OUTPUT_DIR/${FETCH_URL##*/}"
            ;;
        ..)
            FETCH_OUTPUT_DIR='..'
            FETCH_OUTPUT_FILEPATH="$FETCH_OUTPUT_DIR/${FETCH_URL##*/}"
            ;;
        */)
            FETCH_OUTPUT_DIR="${FETCH_PATH%/}"
            FETCH_OUTPUT_FILEPATH="$FETCH_OUTPUT_DIR/${FETCH_URL##*/}"
            ;;
        *)
            FETCH_OUTPUT_DIR="$(dirname "$FETCH_PATH")"
            FETCH_OUTPUT_FILEPATH="$FETCH_PATH"
    esac

    if [ -n "$FETCH_OUTPUT_FILEPATH" ] ; then
        if [ -f "$FETCH_OUTPUT_FILEPATH" ] ; then
            if [ -n "$FETCH_SHA256_EXPECTED" ] ; then
                if [ "$(sha256sum "$FETCH_OUTPUT_FILEPATH" | cut -d ' ' -f1)" = "$FETCH_SHA256_EXPECTED" ] ; then
                    success "$FETCH_OUTPUT_FILEPATH already have been fetched."
                    return 0
                fi
            fi
        fi

        if [ "$NOT_BUFFER" = 1 ] ; then
            FETCH_BUFFER_FILEPATH="$FETCH_OUTPUT_FILEPATH"
        else
            FETCH_UTS="$(date +%s)"

            FETCH_SHA="$(printf '%s\n' "$FETCH_URL:$$:$FETCH_UTS" | sha256sum | cut -d ' ' -f1)"

            FETCH_BUFFER_FILEPATH="$FETCH_OUTPUT_DIR/$FETCH_SHA.tmp"
        fi
    fi

    for FETCH_TOOL in curl wget http lynx aria2c axel
    do
        if command -v "$FETCH_TOOL" > /dev/null ; then
            break
        else
            unset FETCH_TOOL
        fi
    done

    if [ -z "$FETCH_TOOL" ] ; then
        abort 1 "none of curl wget http lynx aria2c axel command was found, please install one of them then try again."
    fi

    if [                -n "$FETCH_OUTPUT_DIR" ] ; then
        if [ !          -d "$FETCH_OUTPUT_DIR" ] ; then
            run install -d "$FETCH_OUTPUT_DIR" || return 1
        fi
    fi

    case $FETCH_TOOL in
        curl)
            unset CURL_OPTIONS

            if [ "$FETCH_SILENT" = 1 ] ; then
                CURL_OPTIONS='--no-progress-meter'
            fi

            if [ -n "$SSL_CERT_FILE" ] ; then
                CURL_OPTIONS="$CURL_OPTIONS --cacert $SSL_CERT_FILE"
            fi

            if [ -n "$NDKPKG_DNS_SERVERS" ] ; then
                CURL_OPTIONS="$CURL_OPTIONS --dns-servers $NDKPKG_DNS_SERVERS"
            fi

            run "curl $CURL_OPTIONS --fail --retry 20 --retry-delay 30 --location -o '$FETCH_BUFFER_FILEPATH' '$FETCH_URL'" ||
            run "curl $CURL_OPTIONS --fail --retry 20 --retry-delay 30 --location -o '$FETCH_BUFFER_FILEPATH' '$FETCH_URI'"
            ;;
        wget)
            run "wget --timeout=60 -O '$FETCH_BUFFER_FILEPATH' '$FETCH_URL'" ||
            run "wget --timeout=60 -O '$FETCH_BUFFER_FILEPATH' '$FETCH_URI'"
            ;;
        http)
            run "http --timeout=60 -o '$FETCH_BUFFER_FILEPATH' '$FETCH_URL'" ||
            run "http --timeout=60 -o '$FETCH_BUFFER_FILEPATH' '$FETCH_URI'"
            ;;
        lynx)
            run "lynx -source '$FETCH_URL' > '$FETCH_BUFFER_FILEPATH'" ||
            run "lynx -source '$FETCH_URI' > '$FETCH_BUFFER_FILEPATH'"
            ;;
        aria2c)
            run "aria2c -d '$FETCH_OUTPUT_DIR' -o '$FETCH_OUTPUT_FILENAME' '$FETCH_URL'" ||
            run "aria2c -d '$FETCH_OUTPUT_DIR' -o '$FETCH_OUTPUT_FILENAME' '$FETCH_URI'"
            ;;
        axel)
            run "axel -o '$FETCH_BUFFER_FILEPATH' '$FETCH_URL'" ||
            run "axel -o '$FETCH_BUFFER_FILEPATH' '$FETCH_URI'"
            ;;
        *)  abort 1 "wfetch() unimplementation: $FETCH_TOOL"
            ;;
    esac

    [ $? -eq 0 ] || return 1

    if [ -n "$FETCH_OUTPUT_FILEPATH" ] ; then
        if [ -n "$FETCH_SHA256_EXPECTED" ] ; then
            FETCH_SHA256_ACTUAL="$(sha256sum "$FETCH_BUFFER_FILEPATH" | cut -d ' ' -f1)"

            if [ "$FETCH_SHA256_ACTUAL" != "$FETCH_SHA256_EXPECTED" ] ; then
                abort 1 "sha256sum mismatch.\n    expect : $FETCH_SHA256_EXPECTED\n    actual : $FETCH_SHA256_ACTUAL\n"
            fi
        fi

        if [ "$NOT_BUFFER" != 1 ] ; then
            run mv "$FETCH_BUFFER_FILEPATH" "$FETCH_OUTPUT_FILEPATH"
        fi
    fi
}

# }}}
##############################################################################
# {{{ version

# check if match the condition
#
# condition:
# eq  equal
# ne  not equal
# gt  greater than
# lt  less than
# ge  greater than or equal
# le  less than or equal
#
# examples:
# version_match 1.15.3 eq 1.16.0
# version_match 1.15.3 lt 1.16.0
# version_match 1.15.3 gt 1.16.0
# version_match 1.15.3 le 1.16.0
# version_match 1.15.3 ge 1.16.0
version_match() {
    case $2 in
        eq)  [ "$1"  = "$3" ] ;;
        ne)  [ "$1" != "$3" ] ;;
        le)
            if [ "$1" = "$3" ] ; then
                return 0
            fi
            [ "$1" = "$(printf '%s\n' "$1" "$3" | sort -V | head -n 1)" ]
            ;;
        ge)
            if [ "$1" = "$3" ] ; then
                return 0
            fi
            [ "$1" = "$(printf '%s\n' "$1" "$3" | sort -V | tail -n 1)" ]
            ;;
        lt)
            if [ "$1" = "$3" ] ; then
                return 1
            fi
            [ "$1" = "$(printf '%s\n' "$1" "$3" | sort -V | head -n 1)" ]
            ;;
        gt)
            if [ "$1" = "$3" ] ; then
                return 1
            fi
            [ "$1" = "$(printf '%s\n' "$1" "$3" | sort -V | tail -n 1)" ]
            ;;
        *)  abort 1 "version_compare: $2: not supported operator."
    esac
}

# }}}
##############################################################################
# {{{ __load_formula_repository_config

# __load_formula_repository_config <REPO-NAME> [REPO-PATH]
  __load_formula_repository_config() {
    FORMULA_REPO_NAME="$1"
    FORMULA_REPO_PATH="$NDKPKG_HOME/repos.d/$1"
    FORMULA_REPO_CONFIG_FILEPATH="$FORMULA_REPO_PATH/.ndk-pkg-formula-repo.yml"

    [ -d "$FORMULA_REPO_PATH" ] || abort 1 "formula repository '$1' does not exist."
    [ -f "$FORMULA_REPO_CONFIG_FILEPATH" ] || abort 1 "formula repository '$1' is broken."

    FORMULA_REPO_URL=
    FORMULA_REPO_BRANCH=
    FORMULA_REPO_PINNED=
    FORMULA_REPO_ENABLED=
    FORMULA_REPO_TIMESTAMP_CREATED=
    FORMULA_REPO_TIMESTAMP_UPDATED=

    FORMULA_REPO_URL="$(yq .url "$FORMULA_REPO_CONFIG_FILEPATH")"

    [ "$FORMULA_REPO_URL" = null ] && abort 1 "formula repository '$1' is broken."

    FORMULA_REPO_BRANCH="$(yq .branch "$FORMULA_REPO_CONFIG_FILEPATH")"

    [ "$FORMULA_REPO_BRANCH" = null ] && abort 1 "formula repository '$1' is broken."

    FORMULA_REPO_PINNED="$(yq .pinned "$FORMULA_REPO_CONFIG_FILEPATH")"

    case $FORMULA_REPO_PINNED in
        0|1) ;;
        *)   abort 1 "formula repository '$1' is broken."
    esac

    FORMULA_REPO_ENABLED="$(yq .enabled "$FORMULA_REPO_CONFIG_FILEPATH")"

    case $FORMULA_REPO_ENABLED in
        0|1) ;;
        *)   abort 1 "formula repository '$1' is broken."
    esac

    FORMULA_REPO_TIMESTAMP_CREATED="$(yq .created "$FORMULA_REPO_CONFIG_FILEPATH")"

    [ "${#FORMULA_REPO_TIMESTAMP_CREATED}" -eq 10 ] || abort 1 "formula repository '$1' is broken."

    FORMULA_REPO_TIMESTAMP_UPDATED="$(yq .updated "$FORMULA_REPO_CONFIG_FILEPATH")"

    if [ "$FORMULA_REPO_TIMESTAMP_UPDATED" = null ] ; then
           FORMULA_REPO_TIMESTAMP_UPDATED=
    else
        [ "${#FORMULA_REPO_TIMESTAMP_UPDATED}" -eq 10 ] || abort 1 "formula repository '$1' is broken."
    fi
}

# }}}
##############################################################################
# {{{ ndk-pkg formula-repo-add

# __create_a_formula_repository <REPO-NAME> <REPO-URL> [--branch=VALUE --pin/--unpin --enable/--disable --sync]
  __create_a_formula_repository() {
    [ -z "$1" ] && abort 1 "please specify a repository name."
    [ -z "$2" ] && abort 1 "please specify a repository url."

    FORMULA_REPO_NAME="$1"
    FORMULA_REPO_URL="$2"

    shift 2

    FORMULA_REPO_BRANCH=
    FORMULA_REPO_PINNED=
    FORMULA_REPO_ENABLED=
    FORMULA_REPO_SYNC=

    while [ -n "$1" ]
    do
        case $1 in
            --branch=*)
                FORMULA_REPO_BRANCH="${1#*=}"
                ;;
            --pin)
                FORMULA_REPO_PINNED=1
                ;;
            --unpin)
                FORMULA_REPO_PINNED=0
                ;;
            --enable)
                FORMULA_REPO_ENABLED=1
                ;;
            --disable)
                FORMULA_REPO_ENABLED=0
                ;;
            --sync)
                FORMULA_REPO_SYNC=1
                ;;
            *)  abort 1 "unrecognized argument: $1"
        esac
        shift
    done

    FORMULA_REPO_BRANCH="${FORMULA_REPO_BRANCH:-master}"
    FORMULA_REPO_PINNED="${FORMULA_REPO_PINNED:-0}"
    FORMULA_REPO_ENABLED="${FORMULA_REPO_ENABLED:-1}"

    FORMULA_REPO_PATH="$NDKPKG_HOME/repos.d/$FORMULA_REPO_NAME"

    if [ -d "$FORMULA_REPO_PATH" ] ; then
        abort 1 "formula repository '$FORMULA_REPO_NAME' already exists."
    fi

    printf '%b\n' "${COLOR_PURPLE}==> ndk-pkg formula repository${COLOR_OFF} ${COLOR_GREEN}$FORMULA_REPO_NAME${COLOR_OFF} ${COLOR_PURPLE} is being added${COLOR_OFF}"

    SESSION_DIR="$NDKPKG_HOME/run/$$/$FORMULA_REPO_NAME"

    run rm -rf     "$SESSION_DIR"
    run install -d "$SESSION_DIR"
    run cd         "$SESSION_DIR"

    if [ "$FORMULA_REPO_SYNC" = 1 ] ; then
        if [ -z "$NDKPKG_URL_TRANSFORM" ] ; then
            GIT_FETCH_URL="$FORMULA_REPO_URL"
        else
            GIT_FETCH_URL="$("$NDKPKG_URL_TRANSFORM" "$FORMULA_REPO_URL")"
        fi

        run git -c init.defaultBranch=master init
        run git remote add origin "$GIT_FETCH_URL"
        run git -c protocol.version=2 fetch --progress origin "+refs/heads/$FORMULA_REPO_BRANCH:refs/remotes/origin/$FORMULA_REPO_BRANCH"
        run git checkout --progress --force -B "$FORMULA_REPO_BRANCH" "refs/remotes/origin/$FORMULA_REPO_BRANCH"
    else
        run git -c init.defaultBranch=master init
        run git remote add origin "$FORMULA_REPO_URL"
    fi

    cat > .ndk-pkg-formula-repo.yml <<EOF
url: $FORMULA_REPO_URL
branch: $FORMULA_REPO_BRANCH
pinned: $FORMULA_REPO_PINNED
enabled: $FORMULA_REPO_ENABLED
created: $TIMESTAMP_UNIX
EOF

    run install -d        "$NDKPKG_FORMULA_REPO_ROOT"
    run mv "$SESSION_DIR" "$NDKPKG_FORMULA_REPO_ROOT/"
}

# }}}
##############################################################################
# {{{ ndk-pkg formula-repo-del

# __delete_a_formula_repository <REPO-NAME>
  __delete_a_formula_repository() {
    [ -z "$1" ] && abort 1 "please specify a repository name."

    [ "$1" = 'official-core' ] && abort 1 "formula repository 'official-core' is not allowed to delete."

    if [ -d    "$NDKPKG_HOME/repos.d/$1" ] ; then
        rm -rf "$NDKPKG_HOME/repos.d/$1"
    else
        abort 1 "formula repository '$1' does not exist."
    fi
}

# }}}
##############################################################################
# {{{ ndk-pkg formula-repo-sync

# __sync_the_given_formula_repository <REPO-NAME>
  __sync_the_given_formula_repository() {
    [ -z "$1" ] && abort 1 "please specify a repository name."

    __load_formula_repository_config "$1"

    [ "$FORMULA_REPO_PINNED" = 1 ] && abort 1 "'formula repository '$FORMULA_REPO_NAME' is pinned."

    if [ -z "$NDKPKG_URL_TRANSFORM" ] ; then
        GIT_FETCH_URL="$FORMULA_REPO_URL"
    else
        GIT_FETCH_URL="$("$NDKPKG_URL_TRANSFORM" "$FORMULA_REPO_URL")"
    fi

    printf '%b\n' "${COLOR_PURPLE}==> ndk-pkg formula repository${COLOR_OFF} ${COLOR_GREEN}$FORMULA_REPO_NAME${COLOR_OFF} ${COLOR_PURPLE}is being updated${COLOR_OFF}"

    run cd "$FORMULA_REPO_PATH"
    run git remote set-url origin "$GIT_FETCH_URL"
    run git -c protocol.version=2 fetch --progress origin "+refs/heads/$FORMULA_REPO_BRANCH:refs/remotes/origin/$FORMULA_REPO_BRANCH"
    run git checkout --progress --force -B "$FORMULA_REPO_BRANCH" "refs/remotes/origin/$FORMULA_REPO_BRANCH"

    cat > .ndk-pkg-formula-repo.yml <<EOF
url: $FORMULA_REPO_URL
branch: $FORMULA_REPO_BRANCH
pinned: $FORMULA_REPO_PINNED
enabled: $FORMULA_REPO_ENABLED
created: $FORMULA_REPO_TIMESTAMP_CREATED
updated: $TIMESTAMP_UNIX
EOF
}

# }}}
##############################################################################
# {{{ ndk-pkg formula-repo-conf

# __conf_the_given_formula_repository <REPO-NAME> [--url=VALUE --branch=VALUE --pin/--unpin --enable/--disable]
  __conf_the_given_formula_repository() {
    [ -z "$1" ] && abort 1 "please specify a repository name."
    [ -z "$2" ] && abort 1 "at least one option should be given. supported options are: --url=VALUE --branch=VALUE --pin/--unpin --enable/--disable"

    __load_formula_repository_config "$1"

    shift

    while [ -n "$1" ]
    do
        case $1 in
            --url=*)
                FORMULA_REPO_URL="${1#*=}"

                if [ -z "$FORMULA_REPO_URL" ] ; then
                    abort 1 "--url=<VALUE> , <VALUE> should be non-empty."
                else
                    FORMULA_REPO_URL="$FORMULA_REPO_URL"
                fi
                ;;
            --branch=*)
                FORMULA_REPO_BRANCH="${1#*=}"

                if [ -z "$FORMULA_REPO_BRANCH" ] ; then
                    abort 1 "--branch=<VALUE> , <VALUE> should be non-empty."
                else
                    FORMULA_REPO_BRANCH="$FORMULA_REPO_BRANCH"
                fi
                ;;
            --pin)
                FORMULA_REPO_PINNED=1
                ;;
            --unpin)
                FORMULA_REPO_PINNED=0
                ;;
            --enable)
                FORMULA_REPO_ENABLED=1
                ;;
            --disable)
                FORMULA_REPO_ENABLED=0
                ;;
            *)  abort 1 "unrecognized argument: $1"
        esac
        shift
    done

    if [ -z "$FORMULA_REPO_TIMESTAMP_UPDATED" ] ; then
        cat > "$FORMULA_REPO_PATH/.ndk-pkg-formula-repo.yml" <<EOF
url:  $FORMULA_REPO_URL
branch: $FORMULA_REPO_BRANCH
pinned:  $FORMULA_REPO_PINNED
enabled: $FORMULA_REPO_ENABLED
created: $FORMULA_REPO_TIMESTAMP_CREATED
EOF
    else
        cat > "$FORMULA_REPO_PATH/.ndk-pkg-formula-repo.yml" <<EOF
url:  $FORMULA_REPO_URL
branch: $FORMULA_REPO_BRANCH
pinned:  $FORMULA_REPO_PINNED
enabled: $FORMULA_REPO_ENABLED
created: $FORMULA_REPO_TIMESTAMP_CREATED
updated: $FORMULA_REPO_TIMESTAMP_UPDATED
EOF
fi
}

# }}}
##############################################################################
# {{{ ndk-pkg formula-repo-info

# __info_the_given_formula_repository <REPO-NAME>
  __info_the_given_formula_repository() {
    [ -z "$1" ] && abort 1 "please specify a repository name."

    __load_formula_repository_config "$1"

    case $FORMULA_REPO_PINNED in
        0)  FORMULA_REPO_PINNED=no ;;
        1)  FORMULA_REPO_PINNED=yes ;;
    esac

    case $FORMULA_REPO_ENABLED in
        0)  FORMULA_REPO_ENABLED=no ;;
        1)  FORMULA_REPO_ENABLED=yes ;;
    esac

    cat <<EOF
name: $FORMULA_REPO_NAME
path: $FORMULA_REPO_PATH
url:  $FORMULA_REPO_URL
branch: $FORMULA_REPO_BRANCH
pinned: $FORMULA_REPO_PINNED
enabled: $FORMULA_REPO_ENABLED
created: $(date -d "@$FORMULA_REPO_TIMESTAMP_CREATED" '+%Y-%m-%d %H:%M:%S%:z')
EOF

    if [ -n "$FORMULA_REPO_TIMESTAMP_UPDATED" ] ; then
        printf 'updated: %s\n' "$(date -d "@$FORMULA_REPO_TIMESTAMP_UPDATED" '+%Y-%m-%d %H:%M:%S%:z')"
    fi
}

# }}}
##############################################################################
# {{{ ndk-pkg update

__sync_available_formula_repositories() {
    [ -d "$NDKPKG_FORMULA_REPO_ROOT" ] && {
        for item in $(cd "$NDKPKG_FORMULA_REPO_ROOT" && ls)
        do
            if [ -f "$NDKPKG_FORMULA_REPO_ROOT/$item/.ndk-pkg-formula-repo.yml" ] ; then
                __sync_the_given_formula_repository "$item"
            fi
        done
    }

    [ -d "$NDKPKG_FORMULA_REPO_ROOT/official-core" ] || {
        __create_a_formula_repository official-core "$NDKPKG_OFFICIAL_FORMULA_REPO_URL" --sync
    }
}

# }}}
##############################################################################
# {{{ ndk-pkg formula-repo-list

__list_available_formula_repositories() {
    [ -d "$NDKPKG_FORMULA_REPO_ROOT" ] || return 0

    I=0

    for item in $(cd "$NDKPKG_FORMULA_REPO_ROOT" && ls)
    do
        if [ -f "$NDKPKG_FORMULA_REPO_ROOT/$item/.ndk-pkg-formula-repo.yml" ] ; then
            I="$(expr "$I" + 1)"

            [ "$I" -gt 1 ] && printf '\n'

            __info_the_given_formula_repository "$item"
        fi
    done
}

# __path_of_formula_of_the_given_package <PACKAGE-NAME>
  __path_of_formula_of_the_given_package() {
    [ -z "$1" ] && abort 1 "__path_of_formula_of_the_given_package <PACKAGE-NAME>, <PACKAGE-NAME> is unspecified."

    for FORMULA_SEARCH_DIR in $NDKPKG_FORMULA_SEARCH_DIRS
    do
        FORMULA_FILEPATH="$FORMULA_SEARCH_DIR/$1.yml"

        if [ -f           "$FORMULA_FILEPATH" ] ; then
            printf '%s\n' "$FORMULA_FILEPATH"
            return 0
        fi
    done

    [ -d "$NDKPKG_FORMULA_REPO_ROOT" ] || return 0

    FORMULA_REPOSITORY_NAMES=

    for item in $(cd "$NDKPKG_FORMULA_REPO_ROOT" && ls)
    do
        if [ -f "$NDKPKG_FORMULA_REPO_ROOT/$item/.ndk-pkg-formula-repo.yml" ] ; then
            FORMULA_REPOSITORY_NAMES="$FORMULA_REPOSITORY_NAMES $item"
        fi
    done

    for FORMULA_REPOSITORY_NAME in $FORMULA_REPOSITORY_NAMES
    do
        FORMULA_FILEPATH="$NDKPKG_FORMULA_REPO_ROOT/$FORMULA_REPOSITORY_NAME/formula/$1.yml"

        if [ -f           "$FORMULA_FILEPATH" ] ; then
            printf '%s\n' "$FORMULA_FILEPATH"
            return 0
        fi
    done
}

# }}}
##############################################################################
# {{{ formula parse

filetype_from_url() {
    # remove query params
    URL="${1%%'?'*}"

    FNAME="${URL##*/}"

    case $FNAME in
        *.tar.gz|*.tgz)
            printf '%s\n' '.tgz'
            ;;
        *.tar.lz|*.tlz)
            printf '%s\n' '.tlz'
            ;;
        *.tar.xz|*.txz)
            printf '%s\n' '.txz'
            ;;
        *.tar.bz2|*.tbz2)
            printf '%s\n' '.tbz2'
            ;;
        *.*)printf '%s\n' ".${FNAME##*.}"
    esac
}

# __load_formula_of_the_given_package <PACKAGE-NAME> [FORMULA-FILEPATH]
  __load_formula_of_the_given_package() {
    if [ -z "$1" ] ; then
        abort 1 "__load_formula_of_the_given_package <PACKAGE-NAME> [FORMULA-FILEPATH], <PACKAGE-NAME> is unspecified."
    else
        PACKAGE_NAME="$1"
        PACKAGE_NAME_UPPERCASE_UNDERSCORE=$(printf '%s\n' "$PACKAGE_NAME" | tr a-z A-Z | tr '@+-.' '_')
    fi

    #########################################################################################

    if [ -z "$2" ] ; then
        PACKAGE_FORMULA_FILEPATH="$(__path_of_formula_of_the_given_package "$1")"

        if [ -z "$PACKAGE_FORMULA_FILEPATH" ] ; then
            abort 1 "package '$1' is not available."
        fi
    else
        PACKAGE_FORMULA_FILEPATH="$2"
    fi

    #########################################################################################

    unset PACKAGE_PKGTYPE

    unset PACKAGE_SUMMARY

    unset PACKAGE_LICENSE

    unset PACKAGE_VERSION
    unset PACKAGE_VERSION_MAJOR
    unset PACKAGE_VERSION_MINOR
    unset PACKAGE_VERSION_PATCH
    unset PACKAGE_VERSION_TWEAK

    unset PACKAGE_WEB_URL

    unset PACKAGE_GIT_URL
    unset PACKAGE_GIT_SHA
    unset PACKAGE_GIT_REF
    unset PACKAGE_GIT_NTH

    unset PACKAGE_SRC_URL
    unset PACKAGE_SRC_URI
    unset PACKAGE_SRC_SHA
    unset PACKAGE_SRC_FILENAME
    unset PACKAGE_SRC_FILETYPE
    unset PACKAGE_SRC_FILEPATH

    unset PACKAGE_FIX_URL
    unset PACKAGE_FIX_URI
    unset PACKAGE_FIX_SHA
    unset PACKAGE_FIX_OPT
    unset PACKAGE_FIX_FILENAME
    unset PACKAGE_FIX_FILETYPE
    unset PACKAGE_FIX_FILEPATH

    unset PACKAGE_RES_URL
    unset PACKAGE_RES_URI
    unset PACKAGE_RES_SHA
    unset PACKAGE_RES_FILENAME
    unset PACKAGE_RES_FILETYPE
    unset PACKAGE_RES_FILEPATH

    # space-separated    perl modules that are depended by this package when installing, which will be installed via cpan
    unset PACKAGE_DEP_PLM

    # space-separated python packages that are depended by this package when installing, which will be installed via pip3
    unset PACKAGE_DEP_PYM

    # space-separated   uppm packages that are depended by this package when installing, which will be installed via uppm
    unset PACKAGE_DEP_UPP

    # space-separated ndk-pkg packages that are depended by this package when installing and/or runtime, which will be installed via ndk-pkg
    unset PACKAGE_DEP_PKG

    unset PACKAGE_DEP_LIB

    unset PACKAGE_PPFLAGS
    unset PACKAGE_CCFLAGS
    unset PACKAGE_XXFLAGS
    unset PACKAGE_LDFLAGS

    unset PACKAGE_ONSTART
    unset PACKAGE_ONREADY
    unset PACKAGE_ONFINAL

    unset PACKAGE_DO12345
    unset PACKAGE_DOPATCH
    unset PACKAGE_PREPARE
    unset PACKAGE_DOBUILD
    unset PACKAGE_DOTWEAK

    unset PACKAGE_PATCHES
    unset PACKAGE_RESLIST

    unset PACKAGE_CAVEATS

    unset PACKAGE_BSYSTEM
    unset PACKAGE_BSYSTEM_MASTER

    unset PACKAGE_USE_BSYSTEM_WAF
    unset PACKAGE_USE_BSYSTEM_GO
    unset PACKAGE_USE_BSYSTEM_RAKE
    unset PACKAGE_USE_BSYSTEM_NINJA
    unset PACKAGE_USE_BSYSTEM_GMAKE
    unset PACKAGE_USE_BSYSTEM_CMAKE
    unset PACKAGE_USE_BSYSTEM_XMAKE
    unset PACKAGE_USE_BSYSTEM_MESON
    unset PACKAGE_USE_BSYSTEM_CARGO
    unset PACKAGE_USE_BSYSTEM_AUTOGENSH
    unset PACKAGE_USE_BSYSTEM_AUTOTOOLS
    unset PACKAGE_USE_BSYSTEM_CONFIGURE
    unset PACKAGE_USE_BSYSTEM_NDK_BUILD

    # directory relative to $PACKAGE_WORKING_DIR/src, which contains build script such as autogen.sh, configure, Makefile, CMakeLists.txt, meson.build, Cargo.toml, xmake.lua, etc.
    unset PACKAGE_BSCRIPT

    # whether to build in build script directory, otherwise build in build dir
    unset PACKAGE_BINBSTD

    # whether to build in parallel
    unset PACKAGE_PARALLEL

    unset PACKAGE_DEVELOPER

    # package support min sdk api level
    unset PACKAGE_API_MIN

    #########################################################################################

    PACKAGE_API_MIN="$(yq '.api-min | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_PKGTYPE="$(yq '.pkgtype | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_SUMMARY="$(yq '.summary | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_LICENSE="$(yq '.license | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_VERSION="$(yq '.version | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_WEB_URL="$(yq '.web-url | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_GIT_URL="$(yq '.git-url | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_GIT_SHA="$(yq '.git-sha | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_GIT_REF="$(yq '.git-ref | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_GIT_NTH="$(yq '.git-nth | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_SRC_URL="$(yq '.src-url | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_SRC_URI="$(yq '.src-uri | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_SRC_SHA="$(yq '.src-sha | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_FIX_URL="$(yq '.fix-url | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_FIX_URI="$(yq '.fix-uri | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_FIX_SHA="$(yq '.fix-sha | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_FIX_OPT="$(yq '.fix-opt | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_RES_URL="$(yq '.res-url | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_RES_URI="$(yq '.res-uri | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_RES_SHA="$(yq '.res-sha | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_DEP_PKG="$(yq '.dep-pkg | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_DEP_LIB="$(yq '.dep-lib | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_DEP_UPP="$(yq '.dep-upp | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_DEP_PYM="$(yq '.dep-pym | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_DEP_PLM="$(yq '.dep-plm | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_BSYSTEM="$(yq '.bsystem | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_BSCRIPT="$(yq '.bscript | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_BINBSTD="$(yq '.binbstd | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_CCFLAGS="$(yq '.ccflags | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_XXFLAGS="$(yq '.xxflags | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_PPFLAGS="$(yq '.ppflags | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_LDFLAGS="$(yq '.ldflags | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_ONSTART="$(yq '.onstart | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_ONREADY="$(yq '.onready | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_ONFINAL="$(yq '.onfinal | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_DO12345="$(yq '.do12345 | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_DOPATCH="$(yq '.dopatch | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_PREPARE="$(yq '.prepare | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_DOBUILD="$(yq '.install | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_DOTWEAK="$(yq '.dotweak | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_PATCHES="$(yq '.patches | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"
    PACKAGE_RESLIST="$(yq '.reslist | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_CAVEATS="$(yq '.caveats | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_PARALLEL="$(yq '.parallel | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    PACKAGE_DEVELOPER="$(yq '.developer | select(. != null)' "$PACKAGE_FORMULA_FILEPATH")"

    #########################################################################################

    if [ -z "$PACKAGE_SUMMARY" ] ; then
        abort 1 "summary mapping not found in $PACKAGE_FORMULA_FILEPATH"
    fi

    if [ -n "$PACKAGE_FIX_URL" ] && [ -n "$PACKAGE_PATCHES" ] ; then
        abort 1 "fix-url and patches mapping shouldn't be used together in $PACKAGE_FORMULA_FILEPATH"
    fi

    if [ -n "$PACKAGE_RES_URL" ] && [ -n "$PACKAGE_RESLIST" ] ; then
        abort 1 "res-url and reslist mapping shouldn't be used together in $PACKAGE_FORMULA_FILEPATH"
    fi

    if [ -n "$PACKAGE_GIT_NTH" ] ; then
        isInteger "$PACKAGE_GIT_NTH" || abort "the value of git-nth mapping should be an integer."
    fi

    if [ -n "$PACKAGE_API_MIN" ] ; then
        isInteger "$PACKAGE_API_MIN" || abort "the value of api-min mapping should be an integer."
    fi

    #########################################################################################

    unset PACKAGE_NEED_CURL
    unset PACKAGE_NEED_BTAR

    #########################################################################################

    if [ -n "$PACKAGE_SRC_URL" ] ; then
        case $PACKAGE_SRC_URL in
            dir://*)
                PACKAGE_SRC_FILETYPE=.dir
                PACKAGE_SRC_FILEPATH=$(printf '%s\n' "$PACKAGE_SRC_URL" | cut -c7-)

                if [ -z "$PACKAGE_VERSION" ] ; then
                    PACKAGE_VERSION="$(date -u -d "@$TIMESTAMP_UNIX" '+%Y.%m.%d')"
                fi
                ;;
            file://*)
                PACKAGE_SRC_FILEPATH=$(printf '%s\n' "$PACKAGE_SRC_URL" | cut -c8-)
                PACKAGE_SRC_FILENAME="$(basename "$PACKAGE_SRC_FILEPATH")"
                PACKAGE_SRC_FILETYPE="$(filetype_from_url "$PACKAGE_SRC_FILENAME")"

                if [ -z "$PACKAGE_VERSION" ] ; then
                    PACKAGE_VERSION="$(date -u -d "@$TIMESTAMP_UNIX" '+%Y.%m.%d')"
                fi
                ;;
            *)  PACKAGE_SRC_FILETYPE="$(filetype_from_url "$PACKAGE_SRC_URL")"
                PACKAGE_SRC_FILENAME="$PACKAGE_SRC_SHA$PACKAGE_SRC_FILETYPE"
                PACKAGE_SRC_FILEPATH="$NDKPKG_DOWNLOADS_DIR/$PACKAGE_SRC_FILENAME"

                if [ -z "$PACKAGE_SRC_SHA" ] ; then
                    abort 1 "src-sha mapping not found in $PACKAGE_FORMULA_FILEPATH"
                fi

                if [ -z "$PACKAGE_GIT_URL" ] ; then
                    case $PACKAGE_SRC_URL in
                        https://github.com/*/*/releases/*)
                            PACKAGE_GIT_URL="${PACKAGE_SRC_URL%%/releases/*}"
                            ;;
                        https://github.com/*/*/archive/*)
                            PACKAGE_GIT_URL="${PACKAGE_SRC_URL%%/archive/*}"
                            ;;
                        https://gitlab.com/*/*/-/archive/*)
                            PACKAGE_GIT_URL="${PACKAGE_SRC_URL%%/-/archive/*}"
                            ;;
                    esac
                fi

                if [ -z "$PACKAGE_VERSION" ] ; then
                    PACKAGE_VERSION="$(basename "$PACKAGE_SRC_URL" | tr '_@' - | sed -e 's|\.tar\.[glx]z$||' -e 's|\.tar\.bz2$||' -e 's|\.t[glx]z$||' -e 's|\.zip$||' -e 's|-stable||' -e 's|-source||' -e 's|[-.]src$||' -e 's|\.orig||' | awk -F- '{print $NF}')"
                    case $PACKAGE_VERSION in
                        '') abort 1 "version mapping not found in $PACKAGE_FORMULA_FILEPATH" ;;
                        v*) PACKAGE_VERSION=$(printf '%s\n' "$PACKAGE_VERSION" | cut -c2-)
                    esac
                fi

                PACKAGE_NEED_CURL=1

                case $PACKAGE_SRC_FILETYPE in
                    .zip|.txz|.tgz|.tlz|.tbz2|.crate)
                        PACKAGE_NEED_BTAR=1
                esac
        esac
    else
        if [ -n "$PACKAGE_GIT_URL" ] ; then
            PACKAGE_SRC_FILETYPE=.git

            if [ -z "$PACKAGE_VERSION" ] ; then
                PACKAGE_VERSION="$(date -u -d "@$TIMESTAMP_UNIX" '+%Y.%m.%d')"
            fi

            PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP git"
        fi
    fi

    #########################################################################################

    if [ -z "$PACKAGE_WEB_URL" ] && [ -z "$PACKAGE_GIT_URL" ] ; then
        abort 1 "neither web-url nor git-url mapping was found in $PACKAGE_FORMULA_FILEPATH"
    fi

    #########################################################################################

    [ -n "$PACKAGE_FIX_URL" ] && {
        [ -z "$PACKAGE_FIX_SHA" ] && abort 1 "fix-sha mapping not found in $PACKAGE_FORMULA_FILEPATH"

        PACKAGE_FIX_FILETYPE="$(filetype_from_url "$PACKAGE_FIX_URL")"
        PACKAGE_FIX_FILENAME="$PACKAGE_FIX_SHA$PACKAGE_FIX_FILETYPE"
        PACKAGE_FIX_FILEPATH="$NDKPKG_DOWNLOADS_DIR/$PACKAGE_FIX_FILENAME"

        PACKAGE_NEED_CURL=1

        case $PACKAGE_SRC_FILETYPE in
            .zip|.txz|.tgz|.tlz|.tbz2|.crate)
                PACKAGE_NEED_BTAR=1
        esac
    }

    #########################################################################################

    [ -n "$PACKAGE_RES_URL" ] && {
        [ -z "$PACKAGE_RES_SHA" ] && abort 1 "res-sha mapping not found in $PACKAGE_FORMULA_FILEPATH"

        PACKAGE_RES_FILETYPE="$(filetype_from_url "$PACKAGE_RES_URL")"
        PACKAGE_RES_FILENAME="$PACKAGE_RES_SHA$PACKAGE_RES_FILETYPE"
        PACKAGE_RES_FILEPATH="$NDKPKG_DOWNLOADS_DIR/$PACKAGE_RES_FILENAME"

        PACKAGE_NEED_CURL=1

        case $PACKAGE_SRC_FILETYPE in
            .zip|.txz|.tgz|.tlz|.tbz2|.crate)
                PACKAGE_NEED_BTAR=1
        esac
    }

    #########################################################################################

    for LINE in $PACKAGE_PATCHES $PACKAGE_RESLIST
    do
        SHA="$(printf '%s\n' "$LINE" | cut -d '|' -f1)"
        URL="$(printf '%s\n' "$LINE" | cut -d '|' -f2)"

        FILETYPE="$(filetype_from_url "$URL")"

        if [ "${#SHA}" -ne 64 ] ; then
            abort 1 "not a sha256sum in line: $LINE in file: $PACKAGE_FORMULA_FILEPATH"
        fi

        PACKAGE_NEED_CURL=1

        case $FILETYPE in
            .zip|.txz|.tgz|.tlz|.tbz2|.crate)
                PACKAGE_NEED_BTAR=1
        esac
    done

    #########################################################################################

    [ "$PACKAGE_NEED_CURL" = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP curl"
    [ "$PACKAGE_NEED_BTAR" = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP bsdtar"

    #########################################################################################

    [ -n "$PACKAGE_VERSION" ] && {
        PACKAGE_VERSION_MAJOR="$(printf '%s\n' "$PACKAGE_VERSION" | cut -d. -f1)"
        PACKAGE_VERSION_MINOR="$(printf '%s\n' "$PACKAGE_VERSION" | cut -d. -f2)"
        PACKAGE_VERSION_PATCH="$(printf '%s\n' "$PACKAGE_VERSION" | cut -d. -f3)"
        PACKAGE_VERSION_TWEAK="$(printf '%s\n' "$PACKAGE_VERSION" | cut -d. -f4)"
    }

    #########################################################################################

    if [     -z "$PACKAGE_BSYSTEM" ] ; then
        if [ -z "$PACKAGE_DOBUILD" ] ; then
            abort 1 "neither bsystem nor install mapping was found in $PACKAGE_FORMULA_FILEPATH"
        else
            for FirstWordOfLineInInstallActions in $(printf '%s\n' "$PACKAGE_DOBUILD" | sed 's|^[[:space:]]*||' | cut -d ' ' -f1)
            do
                case "$FirstWordOfLineInInstallActions" in
                    configure)    PACKAGE_BSYSTEM=configure ; break ;;
                    cmakew)       PACKAGE_BSYSTEM=cmake ; break ;;
                    xmakew)       PACKAGE_BSYSTEM=xmake ; break ;;
                    mesonw)       PACKAGE_BSYSTEM=meson ; break ;;
                    gmakew)       PACKAGE_BSYSTEM=gmake ; break ;;
                    cargow)       PACKAGE_BSYSTEM=cargo ; break ;;
                    go|gow)       PACKAGE_BSYSTEM=go    ; break ;;
                    waf)          PACKAGE_BSYSTEM=waf   ; break ;;
                esac
            done
        fi
    fi

    PACKAGE_BSYSTEM_MASTER="${PACKAGE_BSYSTEM%%' '*}"

    #########################################################################################

    for BSTSTEM in $PACKAGE_BSYSTEM
    do
        case $BSTSTEM in
            ndk-build)   PACKAGE_USE_BSYSTEM_NDK_BUILD=1 ; PACKAGE_USE_BSYSTEM_GMAKE=1 ;;
            autogen)     PACKAGE_USE_BSYSTEM_AUTOGENSH=1 ; PACKAGE_USE_BSYSTEM_GMAKE=1 ;;
            autotools)   PACKAGE_USE_BSYSTEM_AUTOTOOLS=1 ; PACKAGE_USE_BSYSTEM_GMAKE=1 ;;
            configure)   PACKAGE_USE_BSYSTEM_CONFIGURE=1 ; PACKAGE_USE_BSYSTEM_GMAKE=1 ;;
            cmake+gmake) PACKAGE_USE_BSYSTEM_CMAKE=1     ; PACKAGE_USE_BSYSTEM_GMAKE=1 ;;
            cmake+ninja) PACKAGE_USE_BSYSTEM_CMAKE=1     ; PACKAGE_USE_BSYSTEM_NINJA=1 ;;
            cmake)       PACKAGE_USE_BSYSTEM_CMAKE=1     ; PACKAGE_USE_BSYSTEM_NINJA=1 ;;
            xmake)       PACKAGE_USE_BSYSTEM_XMAKE=1     ;;
            meson)       PACKAGE_USE_BSYSTEM_MESON=1     ; PACKAGE_USE_BSYSTEM_NINJA=1 ;;
            ninja)       PACKAGE_USE_BSYSTEM_NINJA=1     ;;
            gmake)       PACKAGE_USE_BSYSTEM_GMAKE=1     ;;
            rake)        PACKAGE_USE_BSYSTEM_RAKE=1      ;;
            cargo)       PACKAGE_USE_BSYSTEM_CARGO=1     ;;
            go)          PACKAGE_USE_BSYSTEM_GO=1        ;;
            waf)         PACKAGE_USE_BSYSTEM_WAF=1       ;;
        esac
    done

    #########################################################################################

    [ -z "$PACKAGE_DOBUILD" ] && {
        case $PACKAGE_BSYSTEM_MASTER in
            autogen)   PACKAGE_DOBUILD='configure' ;;
            autotools) PACKAGE_DOBUILD='configure' ;;
            configure) PACKAGE_DOBUILD='configure' ;;
            cmake*)    PACKAGE_DOBUILD='cmakew' ;;
            xmake)     PACKAGE_DOBUILD='xmakew' ;;
            meson)     PACKAGE_DOBUILD='mesonw' ;;
            ninja)     PACKAGE_DOBUILD='ninjaw clean && ninjaw && ninjaw install' ;;
            gmake)     PACKAGE_DOBUILD='gmakew clean && gmakew && gmakew install' ;;
            cargo)     PACKAGE_DOBUILD='cargow install' ;;
            go)        PACKAGE_DOBUILD='gow' ;;
            waf)       PACKAGE_DOBUILD='waf' ;;
        esac
    }

    #########################################################################################

    if [ -n "$PACKAGE_FIX_URL" ] || [ -n "$PACKAGE_PATCHES" ] ; then
        PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP patch"
    fi

    [ -n "$PACKAGE_DEP_PYM" ]                && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP python3"
    [ -n "$PACKAGE_DEP_PLM" ]                && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP perl gmake"

    [ "$PACKAGE_USE_BSYSTEM_NDK_BUILD" = 1 ] && PACKAGE_DEP_PYM="$PACKAGE_DEP_PYM gmake"
    [ "$PACKAGE_USE_BSYSTEM_AUTOGENSH" = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP gmake autotools"
    [ "$PACKAGE_USE_BSYSTEM_AUTOTOOLS" = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP gmake autotools"
    [ "$PACKAGE_USE_BSYSTEM_CONFIGURE" = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP gmake"
    [ "$PACKAGE_USE_BSYSTEM_MESON"     = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP python3"
    [ "$PACKAGE_USE_BSYSTEM_MESON"     = 1 ] && PACKAGE_DEP_PYM="$PACKAGE_DEP_PYM meson"
    [ "$PACKAGE_USE_BSYSTEM_CMAKE"     = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP cmake"
    [ "$PACKAGE_USE_BSYSTEM_GMAKE"     = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP gmake"
    [ "$PACKAGE_USE_BSYSTEM_XMAKE"     = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP xmake"
    [ "$PACKAGE_USE_BSYSTEM_NINJA"     = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP ninja"
    [ "$PACKAGE_USE_BSYSTEM_RAKE"      = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP ruby"
    [ "$PACKAGE_USE_BSYSTEM_GO"        = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP golang"
    [ "$PACKAGE_USE_BSYSTEM_WAF"       = 1 ] && PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP python3"

    #########################################################################################

    [ "$PACKAGE_USE_BSYSTEM_WAF"   = 1 ] && PACKAGE_BINBSTD=1
    [ "$PACKAGE_USE_BSYSTEM_GO"    = 1 ] && PACKAGE_BINBSTD=1
    [ "$PACKAGE_USE_BSYSTEM_CARGO" = 1 ] && PACKAGE_BINBSTD=1
    [ "$PACKAGE_USE_BSYSTEM_XMAKE" = 1 ] && PACKAGE_BINBSTD=1

    if [ -z  "$PACKAGE_BINBSTD" ] ; then
        if [ "$PACKAGE_BSYSTEM_MASTER" = gmake ] ; then
            PACKAGE_BINBSTD=1
        else
            PACKAGE_BINBSTD=0
        fi
    fi

    #########################################################################################

    if [ -z "$PACKAGE_PARALLEL" ] ; then
        PACKAGE_PARALLEL=1
    fi

    #########################################################################################

    PACKAGE_DEP_UPP="${PACKAGE_DEP_UPP#' '}"

    #########################################################################################

    if [ -z "$PACKAGE_PKGTYPE" ] ; then
        case $PACKAGE_NAME in
            lib*)   PACKAGE_PKGTYPE=lib ;;
            *lib)   PACKAGE_PKGTYPE=lib ;;
            *-dev)  PACKAGE_PKGTYPE=lib ;;
               *)
                # https://github.com/golang/go/issues/59942
                if [ "$PACKAGE_USE_BSYSTEM_GO" = 1 ] ; then
                    PACKAGE_PKGTYPE=pie
                else
                    PACKAGE_PKGTYPE=exe
                fi
        esac
    fi
}

# }}}
##############################################################################
# {{{ operations of receipt

# __generate_receipt_of_the_given_package <PACKAGE-NAME>
  __generate_receipt_of_the_given_package() {
    [ -z "$1" ] && {
        error "__generate_receipt_of_the_given_package <PACKAGE-NAME>, <PACKAGE-NAME> is unspecified."
        return 1
    }

    cp "$PACKAGE_FORMULA_FILEPATH" "$PACKAGE_RECEIPT_FILEPATH"

    sed -i '/^#src-url: dir:/d' "$PACKAGE_RECEIPT_FILEPATH"

    sed -i "1i pkgname: $PACKAGE_NAME" "$PACKAGE_RECEIPT_FILEPATH"

    grep -q '^pkgtype: ' "$PACKAGE_RECEIPT_FILEPATH" || sed -i "/^pkgname:/a pkgtype: $PACKAGE_PKGTYPE" "$PACKAGE_RECEIPT_FILEPATH"
    grep -q '^version: ' "$PACKAGE_RECEIPT_FILEPATH" || sed -i "/^pkgtype:/a version: $PACKAGE_VERSION" "$PACKAGE_RECEIPT_FILEPATH"
    grep -q '^web-url: ' "$PACKAGE_RECEIPT_FILEPATH" || sed -i "/^summary:/a web-url: $PACKAGE_GIT_URL" "$PACKAGE_RECEIPT_FILEPATH"
    grep -q '^git-url: ' "$PACKAGE_RECEIPT_FILEPATH" || sed -i "/^web-url:/a git-url: $PACKAGE_GIT_URL" "$PACKAGE_RECEIPT_FILEPATH"
    grep -q '^bsystem: ' "$PACKAGE_RECEIPT_FILEPATH" || sed -i "/^install:/i bsystem: $PACKAGE_BSYSTEM" "$PACKAGE_RECEIPT_FILEPATH"
    grep -q '^binbstd: ' "$PACKAGE_RECEIPT_FILEPATH" || sed -i "/^bsystem:/a binbstd: $PACKAGE_BINBSTD" "$PACKAGE_RECEIPT_FILEPATH"
    grep -q '^parallel: ' "$PACKAGE_RECEIPT_FILEPATH" || sed -i "\$a parallel: $PACKAGE_PARALLEL" "$PACKAGE_RECEIPT_FILEPATH"

    [ -n "$PACKAGE_GIT_SHA" ] && {
        grep -q '^git-sha: ' "$PACKAGE_RECEIPT_FILEPATH" || {
            sed -i "/^git-url:/a git-sha: $PACKAGE_GIT_SHA" "$PACKAGE_RECEIPT_FILEPATH"
        }

        grep -q '^git-sha: ' "$PACKAGE_RECEIPT_FILEPATH" || {
            sed -i "3i git-sha: $PACKAGE_GIT_SHA" "$PACKAGE_RECEIPT_FILEPATH"
        }
    }

    [ -n "$PACKAGE_DEP_UPP" ] && {
        if grep -q '^dep-upp: ' "$PACKAGE_RECEIPT_FILEPATH" ; then
            sed -i "/^dep-upp: /c dep-upp: $PACKAGE_DEP_UPP" "$PACKAGE_RECEIPT_FILEPATH"
        else
            sed -i "/^bsystem: /i dep-upp: $PACKAGE_DEP_UPP" "$PACKAGE_RECEIPT_FILEPATH"
        fi
    }

    cat >> "$PACKAGE_RECEIPT_FILEPATH" <<EOF
ndkvers: $ANDROID_NDK_VERSION
builtby: ndk-pkg-$NDKPKG_VERSION
builtat: $TIMESTAMP_UNIX
buildon:
    os-arch: $NATIVE_PLATFORM_ARCH
    os-kind: $NATIVE_PLATFORM_KIND
    os-type: $NATIVE_PLATFORM_TYPE
    os-code: $NATIVE_PLATFORM_CODE
    os-name: $NATIVE_PLATFORM_NAME
    os-vers: $NATIVE_PLATFORM_VERS
    os-ncpu: $NATIVE_PLATFORM_NCPU
    os-euid: $NATIVE_PLATFORM_EUID
    os-egid: $NATIVE_PLATFORM_EGID
builtfor: $TARGET_PLATFORM_SPEC
profile: $PROFILE
EOF
}

# __load_receipt_of_the_given_package <PACKAGE-NAME|PACKAGE-SPEC>
  __load_receipt_of_the_given_package() {
    PACKAGE_SPEC=
    PACKAGE_SPEC="$(inspect_package_spec "$1")"

    is_package_installed "$PACKAGE_SPEC" || return $?

    PACKAGE_INSTALLED_DIR="$(readlink -f "$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC")"

    PACKAGE_RECEIPT_FILEPATH="$PACKAGE_INSTALLED_DIR/.ndk-pkg/RECEIPT.yml"

    unset RECEIPT_PACKAGE_PKGNAME
    unset RECEIPT_PACKAGE_PKGTYPE

    unset RECEIPT_PACKAGE_PROFILE

    unset RECEIPT_PACKAGE_SUMMARY
    unset RECEIPT_PACKAGE_VERSION
    unset RECEIPT_PACKAGE_LICENSE

    unset RECEIPT_PACKAGE_WEB_URL

    unset RECEIPT_PACKAGE_GIT_URL
    unset RECEIPT_PACKAGE_GIT_SHA
    unset RECEIPT_PACKAGE_GIT_REF
    unset RECEIPT_PACKAGE_GIT_NTH

    unset RECEIPT_PACKAGE_SRC_URL
    unset RECEIPT_PACKAGE_SRC_URI
    unset RECEIPT_PACKAGE_SRC_SHA

    unset RECEIPT_PACKAGE_FIX_URL
    unset RECEIPT_PACKAGE_FIX_URI
    unset RECEIPT_PACKAGE_FIX_SHA
    unset RECEIPT_PACKAGE_FIX_OPT

    unset RECEIPT_PACKAGE_RES_URL
    unset RECEIPT_PACKAGE_RES_URI
    unset RECEIPT_PACKAGE_RES_SHA

    unset RECEIPT_PACKAGE_PATCHES
    unset RECEIPT_PACKAGE_RESLIST

    unset RECEIPT_PACKAGE_DEP_PKG
    unset RECEIPT_PACKAGE_DEP_LIB
    unset RECEIPT_PACKAGE_DEP_UPP
    unset RECEIPT_PACKAGE_DEP_PYM
    unset RECEIPT_PACKAGE_DEP_PLM

    unset RECEIPT_PACKAGE_BSYSTEM
    unset RECEIPT_PACKAGE_BSCRIPT

    unset RECEIPT_PACKAGE_ONSTART
    unset RECEIPT_PACKAGE_ONREADY
    unset RECEIPT_PACKAGE_ONFINAL

    unset RECEIPT_PACKAGE_DO12345
    unset RECEIPT_PACKAGE_DOPATCH
    unset RECEIPT_PACKAGE_PREPARE
    unset RECEIPT_PACKAGE_DOBUILD
    unset RECEIPT_PACKAGE_DOTWEAK

    unset RECEIPT_PACKAGE_CAVEATS

    unset RECEIPT_PACKAGE_BUILTBY
    unset RECEIPT_PACKAGE_BUILTAT

    unset RECEIPT_PACKAGE_BUILTFOR
    unset RECEIPT_PACKAGE_BUILTFOR_PLATFORM
    unset RECEIPT_PACKAGE_BUILTFOR_PLATFORM_NAME
    unset RECEIPT_PACKAGE_BUILTFOR_PLATFORM_VERS
    unset RECEIPT_PACKAGE_BUILTFOR_PLATFORM_ARCH

    unset RECEIPT_PACKAGE_PARALLEL
    unset RECEIPT_PACKAGE_DEVELOPER

    unset RECEIPT_PACKAGE_API_MIN
    unset RECEIPT_PACKAGE_NDKVERS

    #########################################################################################

    RECEIPT_PACKAGE_PKGNAME="$(yq '.pkgname | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_PKGTYPE="$(yq '.pkgtype | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_PROFILE="$(yq '.profile | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_SUMMARY="$(yq '.summary | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_LICENSE="$(yq '.license | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_VERSION="$(yq '.version | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_WEB_URL="$(yq '.web-url | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_GIT_URL="$(yq '.git-url | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_GIT_SHA="$(yq '.git-sha | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_GIT_REF="$(yq '.git-ref | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_GIT_NTH="$(yq '.git-nth | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_SRC_URL="$(yq '.src-url | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_SRC_URI="$(yq '.src-uri | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_SRC_SHA="$(yq '.src-sha | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_FIX_URL="$(yq '.fix-url | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_FIX_URI="$(yq '.fix-uri | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_FIX_SHA="$(yq '.fix-sha | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_FIX_OPT="$(yq '.fix-opt | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_RES_URL="$(yq '.res-url | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_RES_URI="$(yq '.res-uri | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_RES_SHA="$(yq '.res-sha | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_RESLIST="$(yq '.reslist | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_PATCHES="$(yq '.patches | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_DEP_PKG="$(yq '.dep-pkg | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_DEP_LIB="$(yq '.dep-lib | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_DEP_UPP="$(yq '.dep-upp | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_DEP_PYM="$(yq '.dep-pym | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_DEP_PLM="$(yq '.dep-plm | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_BSYSTEM="$(yq '.bsystem | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_BSCRIPT="$(yq '.bscript | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_BINBSTD="$(yq '.binbstd | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_CCFLAGS="$(yq '.ccflags | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_XXFLAGS="$(yq '.xxflags | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_PPFLAGS="$(yq '.ppflags | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_LDFLAGS="$(yq '.ldflags | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_ONSTART="$(yq '.onstart | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_ONREADY="$(yq '.onready | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_ONFINAL="$(yq '.onfinal | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_DO12345="$(yq '.do12345 | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_DOPATCH="$(yq '.dopatch | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_PREPARE="$(yq '.prepare | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_DOBUILD="$(yq '.install | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_DOTWEAK="$(yq '.dotweak | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_CAVEATS="$(yq '.caveats | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_API_MIN="$(yq '.api-min | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_NDKVERS="$(yq '.ndkvers | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_BUILTBY="$(yq '.builtby | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"
    RECEIPT_PACKAGE_BUILTAT="$(yq '.builtat | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_BUILTFOR="$(yq '.builtfor | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_PARALLEL="$(yq '.parallel | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    RECEIPT_PACKAGE_DEVELOPER="$(yq '.developer | select(. != null)' "$PACKAGE_RECEIPT_FILEPATH")"

    #########################################################################################

    [ -z "$RECEIPT_PACKAGE_PKGNAME" ] && abort 1 "receipt scheme error. pkgname mapping was not found in $PACKAGE_RECEIPT_FILEPATH."
    [ -z "$RECEIPT_PACKAGE_PKGTYPE" ] && abort 1 "receipt scheme error. pkgtype mapping was not found in $PACKAGE_RECEIPT_FILEPATH."
    [ -z "$RECEIPT_PACKAGE_VERSION" ] && abort 1 "receipt scheme error. version mapping was not found in $PACKAGE_RECEIPT_FILEPATH."
    [ -z "$RECEIPT_PACKAGE_SUMMARY" ] && abort 1 "receipt scheme error. summary mapping was not found in $PACKAGE_RECEIPT_FILEPATH."
    [ -z "$RECEIPT_PACKAGE_WEB_URL" ] && abort 1 "receipt scheme error. web-url mapping was not found in $PACKAGE_RECEIPT_FILEPATH."
    [ -z "$RECEIPT_PACKAGE_BUILTBY" ] && abort 1 "receipt scheme error. builtby mapping was not found in $PACKAGE_RECEIPT_FILEPATH."
    [ -z "$RECEIPT_PACKAGE_BUILTAT" ] && abort 1 "receipt scheme error. builtat mapping was not found in $PACKAGE_RECEIPT_FILEPATH."

    [ "${#RECEIPT_PACKAGE_BUILTAT}" -eq 10 ] || abort 1 "receipt scheme error. builtat mapping's value length must be 10."

    if [ "${PACKAGE_SPEC#*/}" != "$RECEIPT_PACKAGE_PKGNAME" ] ; then
        error "$PACKAGE_RECEIPT_FILEPATH is broken. value of RECEIPT_PACKAGE_PKGNAME does not identical with your request: ${PACKAGE_SPEC#*/}"
        return 1
    fi

    if [ "${PACKAGE_SPEC%/*}" != "$RECEIPT_PACKAGE_BUILTFOR" ] ; then
        error "$PACKAGE_RECEIPT_FILEPATH is broken. value of RECEIPT_PACKAGE_BUILTFOR does not identical with your request: ${PACKAGE_SPEC%/*}"
        return 1
    fi

    #########################################################################################

    RECEIPT_PACKAGE_BUILTFOR_PLATFORM="$RECEIPT_PACKAGE_BUILTFOR"

    case $RECEIPT_PACKAGE_BUILTFOR in
        android-[1-9][0-9]-armeabi-v7a)
            RECEIPT_PACKAGE_BUILTFOR_PLATFORM_ARCH='armv7a'
            ;;
        android-[1-9][0-9]-arm64-v8a)
            RECEIPT_PACKAGE_BUILTFOR_PLATFORM_ARCH='aarch64'
            ;;
        android-[1-9][0-9]-x86)
            RECEIPT_PACKAGE_BUILTFOR_PLATFORM_ARCH='i686'
            ;;
        android-[1-9][0-9]-x86_64)
            RECEIPT_PACKAGE_BUILTFOR_PLATFORM_ARCH='x86_64'
            ;;
        *)  abort 1 "$PACKAGE_RECEIPT_FILEPATH is broken. value of RECEIPT_PACKAGE_BUILTFOR is invalid: $RECEIPT_PACKAGE_BUILTFOR"
    esac

    RECEIPT_PACKAGE_BUILTFOR_PLATFORM_VERS="$(printf '%s\n' "$RECEIPT_PACKAGE_BUILTFOR" | cut -d- -f2)"

    RECEIPT_PACKAGE_BUILTFOR_PLATFORM_NAME=android
}

# }}}
##############################################################################
# {{{ operations of manifest

# __generate_manifest_of_the_given_package <PACKAGE-NAME>
  __generate_manifest_of_the_given_package() {
    # fuck, some package's filename has space
    export IFS='
'

    exec 7> "$PACKAGE_MANIFEST_FILEPATH"

    for d in *
    do
        [ -d "$d" ] || continue

        for item in $(find "$d" -printf '%y:%p\n')
        do
            X=$(printf '%s\n' "$item" | cut -c1)
            Y=${item#$X:}

            case $X in
                l)  if [ -d "$Y" ] ; then
                        printf 'D|0000000000000000000000000000000000000000000000000000000000000000|%s/\n' "$Y" >&7
                    else
                        printf 'l|%s|%s\n' "$(sha256sum "$Y" | cut -d ' ' -f1)"                           "$Y" >&7
                    fi
                    ;;
                d)  printf 'd|0000000000000000000000000000000000000000000000000000000000000000|%s/\n' "$Y" >&7 ;;
                *)  printf '%s|%s|%s\n' "$X" "$(sha256sum "$Y" | cut -d ' ' -f1)"                     "$Y" >&7 ;;
            esac
        done
    done

    exec 7>&-

    unset IFS
}

# }}}
##############################################################################
# {{{ ndk-pkg is-available

# check if the given package is available
# if the version condition is given, check if the condition is matched
#
# condition operator:
# eq  equal
# ne  not equal
# gt  greater than
# lt  less than
# ge  greater than or equal
# le  less than or equal
#
# examples:
# is_package_available automake eq 1.16.0
# is_package_available automake lt 1.16.0
# is_package_available automake gt 1.16.0
# is_package_available automake le 1.16.0
# is_package_available automake ge 1.16.0
# is_package_available automake
is_package_available() {
    case $# in
        0)  abort 1 "package name is unspecified." ;;
        1)  [ -n "$(__path_of_formula_of_the_given_package "$1")" ] ;;
        3)  __load_formula_of_the_given_package "$1"
            shift
            version_match "$PACKAGE_VERSION" "$@"
            ;;
        *)  abort 1 "is available command only accept 1 or 3 argument."
    esac
}

# }}}
##############################################################################
# {{{ ndk-pkg is-installed

# is_package_installed <PACKAGE-SPEC>
  is_package_installed() {
    (
        PACKAGE_INSTALL_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$1"

        [ -L "$PACKAGE_INSTALL_DIR" ] || exit 11
        [ -d "$PACKAGE_INSTALL_DIR" ] || exit 12
        [ -f "$PACKAGE_INSTALL_DIR/.ndk-pkg/MANIFEST.txt" ] || exit 14
        [ -f "$PACKAGE_INSTALL_DIR/.ndk-pkg/RECEIPT.yml" ] || exit 15
    )
}

# }}}
##############################################################################
# {{{ ndk-pkg is-outdated

# is_package__outdated <PACKAGE-SPEC>
  is_package__outdated() {
    __load_receipt_of_the_given_package "$1"
    __load_formula_of_the_given_package "${1#*/}"
    version_match "$PACKAGE_VERSION" gt "$RECEIPT_PACKAGE_VERSION"
}

# }}}
##############################################################################
# {{{ ndk-pkg ls-available [-v] [--yaml | --json]

__list_available_packages() {
    unset OUTPUT_MODE
    unset OUTPUT_TYPE

    for arg in $@
    do
        case $arg in
            -v)     OUTPUT_MODE=full ;;
            --json) OUTPUT_TYPE=json ;;
            --yaml) OUTPUT_TYPE=yaml ;;
            *) abort 1 "unrecognized argument: $arg"
        esac
    done

    if [ -z "$OUTPUT_MODE" ] ; then
        __list_available_package_names
    else
        AVAILABLE_PACKAGE_NAMES="$(__list_available_package_names)"

        [ -z "$AVAILABLE_PACKAGE_NAMES" ] && return 0

        IS_FIRST_ELEMENT=1

        case $OUTPUT_TYPE in
            yaml|'')
                for PKGNAME in $AVAILABLE_PACKAGE_NAMES
                do
                    if [ "$IS_FIRST_ELEMENT" = 1 ] ; then
                        unset IS_FIRST_ELEMENT
                    else
                        printf '%s\n' '---'
                    fi
                    __info_the_given_available_package_as_yaml "$PKGNAME"
                done
                ;;
            json)
                printf '%s\n' '['

                for PKGNAME in $AVAILABLE_PACKAGE_NAMES
                do
                    if [ "$IS_FIRST_ELEMENT" = 1 ] ; then
                        unset IS_FIRST_ELEMENT
                    else
                        printf '%s\n' ','
                    fi
                    __info_the_given_available_package_as_json "$PKGNAME"
                done

                printf '%s\n' ']'
                ;;
        esac
    fi
}

__list_available_package_names() {
    [ -d "$NDKPKG_FORMULA_REPO_ROOT" ] || return 0

    {
        FORMULA_REPOSITORY_NAMES=

        for item in $(cd "$NDKPKG_FORMULA_REPO_ROOT" && ls)
        do
            if [ -f "$NDKPKG_FORMULA_REPO_ROOT/$item/.ndk-pkg-formula-repo.yml" ] ; then
                FORMULA_REPOSITORY_NAMES="$FORMULA_REPOSITORY_NAMES $item"
            fi
        done

        for FORMULA_REPOSITORY_NAME in $FORMULA_REPOSITORY_NAMES
        do
            FORMULA_SEARCH_DIR="$NDKPKG_FORMULA_REPO_ROOT/$FORMULA_REPOSITORY_NAME/formula"

            if [ -d  "$FORMULA_SEARCH_DIR" ] ; then
                find "$FORMULA_SEARCH_DIR" -maxdepth 1 -type f -name '*.yml' -exec basename {} .yml \; || return 1
            fi
        done
    } | sort | uniq
}

# }}}
##############################################################################
# {{{ ndk-pkg ls-installed

__list_installed_packages() {
    if [ -d "$NDKPKG_PACKAGE_INSTALLED_ROOT" ] ; then
         cd "$NDKPKG_PACKAGE_INSTALLED_ROOT" || return 1
        find . -maxdepth 2 -mindepth 2 -type l -regex '\./android-[1-9][0-9]-\(armeabi-v7a\|arm64-v8a\|x86\|x86_64\)/[^/]*' -printf '%P\n'
    else
        return 0
    fi
}

# }}}
##############################################################################
# {{{ ndk-pkg ls-outdated

__list__outdated_packages() {
    for PACKAGE_SPEC in $(__list_installed_packages)
    do
        if is_package__outdated "$PACKAGE_SPEC" ; then
            printf '%s\n' "$PACKAGE_SPEC"
        fi
    done
}

# }}}
##############################################################################
# {{{ ndk-pkg search <REGULAR-EXPRESSION-PATTERN> [-v]

__search_packages() {
    [ -z "$1" ] && abort 1 "please specify a regular express partten."

    AVAILABLE_PACKAGE_NAMES_ALL="$(__list_available_package_names)"

    [ -z "$AVAILABLE_PACKAGE_NAMES_ALL" ] && return 0

    AVAILABLE_PACKAGE_NAMES_FILTERED="$(printf '%s\n' $AVAILABLE_PACKAGE_NAMES_ALL | grep "$1" || true)"

    [ -z "$AVAILABLE_PACKAGE_NAMES_FILTERED" ] && return 0

    case $2 in
        '') printf '%s\n' $AVAILABLE_PACKAGE_NAMES_FILTERED ;;
        -v) IS_FIRST_ELEMENT=1

            for PKGNAME in $AVAILABLE_PACKAGE_NAMES_FILTERED
            do
                if [ "$IS_FIRST_ELEMENT" = 1 ] ; then
                    unset IS_FIRST_ELEMENT
                else
                    printf '%s\n' '---'
                fi

                __info_the_given_available_package_as_yaml "$PKGNAME"
            done
            ;;
        *)  abort 1 "unrecognzied argument: $2"
    esac
}

# }}}
##############################################################################
# {{{ ndk-pkg info

# __info_the_given_available_package_as_json <PACKAGE-NAME>
  __info_the_given_available_package_as_json() {
    __load_formula_of_the_given_package "$1"

    if [ -z "$PACKAGE_WEB_URL" ] ; then
        PACKAGE_WEB_URL="$PACKAGE_GIT_URL"
    fi

    jq  --null-input \
        --arg pkgname "$PACKAGE_NAME" \
        --arg pkgtype "$PACKAGE_PKGTYPE" \
        --arg version "$PACKAGE_VERSION" \
        --arg summary "$PACKAGE_SUMMARY" \
        --arg license "$PACKAGE_LICENSE" \
        --arg web_url "$PACKAGE_WEB_URL" \
        --arg git_url "$PACKAGE_GIT_URL" \
        --arg git_sha "$PACKAGE_GIT_SHA" \
        --arg git_ref "$PACKAGE_GIT_REF" \
        --arg git_nth "$PACKAGE_GIT_NTH" \
        --arg src_url "$PACKAGE_SRC_URL" \
        --arg src_uri "$PACKAGE_SRC_URI" \
        --arg src_sha "$PACKAGE_SRC_SHA" \
        --arg fix_url "$PACKAGE_FIX_URL" \
        --arg fix_uri "$PACKAGE_FIX_URI" \
        --arg fix_sha "$PACKAGE_FIX_SHA" \
        --arg res_url "$PACKAGE_RES_URL" \
        --arg res_uri "$PACKAGE_RES_URI" \
        --arg res_sha "$PACKAGE_RES_SHA" \
        --arg patches "$PACKAGE_PATCHES" \
        --arg reslist "$PACKAGE_RESLIST" \
        --arg dep_pkg "$PACKAGE_DEP_PKG" \
        --arg dep_lib "$PACKAGE_DEP_LIB" \
        --arg dep_upp "$PACKAGE_DEP_UPP" \
        --arg dep_pym "$PACKAGE_DEP_PYM" \
        --arg dep_plm "$PACKAGE_DEP_PLM" \
        --arg onstart "$PACKAGE_ONSTART" \
        --arg onready "$PACKAGE_ONREADY" \
        --arg onfinal "$PACKAGE_ONFINAL" \
        --arg do12345 "$PACKAGE_DO12345" \
        --arg dopatch "$PACKAGE_DOPATCH" \
        --arg prepare "$PACKAGE_PREPARE" \
        --arg install "$PACKAGE_DOBUILD" \
        --arg dotweak "$PACKAGE_DOTWEAK" \
        --arg caveats "$PACKAGE_CAVEATS" \
        --arg bsystem "$PACKAGE_BSYSTEM" \
        --arg binbstd "$PACKAGE_BINBSTD" \
        --arg ccflags "$PACKAGE_CCFLAGS" \
        --arg xxflags "$PACKAGE_XXFLAGS" \
        --arg ppflags "$PACKAGE_PPFLAGS" \
        --arg ldflags "$PACKAGE_LDFLAGS" \
        --arg api_min "$PACKAGE_API_MIN" \
        --arg parallel $PACKAGE_PARALLEL \
        --arg developer "$PACKAGE_DEVELOPER" \
'{
    "pkgname":$pkgname,
    "pkgtype":$pkgtype,
    "version":$version,
    "license":$license,
    "summary":$summary,
    "web-url":$web_url,
    "git-url":$git_url,
    "git-sha":$git_sha,
    "git-ref":$git_ref,
    "git-nth":$git_nth,
    "src-url":$src_url,
    "src-uri":$src_uri,
    "src-sha":$src_sha,
    "fix-url":$fix_url,
    "fix-uri":$fix_uri,
    "fix-sha":$fix_sha,
    "res-url":$res_url,
    "res-uri":$res_uri,
    "res-sha":$res_sha,
    "patches":$patches,
    "reslist":$reslist,
    "dep-pkg":$dep_pkg,
    "dep-lib":$dep_lib,
    "dep-upp":$dep_upp,
    "dep-pym":$dep_pym,
    "dep-plm":$dep_plm,
    "bsystem":$bsystem,
    "binbstd":$binbstd,
    "ccflags":$ccflags,
    "xxflags":$xxflags,
    "ppflags":$ppflags,
    "ldflags":$ldflags,
    "api-min":$api_min,
    "parallel":$parallel,
    "developer":$developer,
    "onstart":$onstart,
    "onready":$onready,
    "onfinal":$onfinal,
    "do12345":$do12345,
    "dopatch":$dopatch,
    "prepare":$prepare,
    "install":$install,
    "dotweak":$dotweak,
    "caveats":$caveats
}' | jq 'with_entries(select(.value != ""))'
}

# __info_the_given_available_package_as_yaml <PACKAGE-NAME>
  __info_the_given_available_package_as_yaml() {
    __load_formula_of_the_given_package "$1"

    if [ -z "$PACKAGE_WEB_URL" ] ; then
        PACKAGE_WEB_URL="$PACKAGE_GIT_URL"
    fi

    if is_package_installed "$1" ; then
        PACKAGE_INSTALLED=yes
    else
        PACKAGE_INSTALLED=no
    fi

    {
    cat <<EOF
pkgname: $PACKAGE_NAME
pkgtype: $PACKAGE_PKGTYPE
version: $PACKAGE_VERSION
license: $PACKAGE_LICENSE
summary: $PACKAGE_SUMMARY
web-url: $PACKAGE_WEB_URL
git-url: $PACKAGE_GIT_URL
git-sha: $PACKAGE_GIT_SHA
git-ref: $PACKAGE_GIT_REF
git-nth: $PACKAGE_GIT_NTH
src-url: $PACKAGE_SRC_URL
src-uri: $PACKAGE_SRC_URI
src-sha: $PACKAGE_SRC_SHA
fix-url: $PACKAGE_FIX_URL
fix-uri: $PACKAGE_FIX_URI
fix-sha: $PACKAGE_FIX_SHA
res-url: $PACKAGE_RES_URL
res-uri: $PACKAGE_RES_URI
res-sha: $PACKAGE_RES_SHA
dep-pkg: $PACKAGE_DEP_PKG
dep-lib: $PACKAGE_DEP_LIB
dep-upp: $PACKAGE_DEP_UPP
dep-pym: $PACKAGE_DEP_PYM
dep-plm: $PACKAGE_DEP_PLM
bsystem: $PACKAGE_BSYSTEM
binbstd: $PACKAGE_BINBSTD
ppflags: $PACKAGE_PPFLAGS
ccflags: $PACKAGE_CCFLAGS
xxflags: $PACKAGE_XXFLAGS
ldflags: $PACKAGE_LDFLAGS
api-min: $PACKAGE_API_MIN
parallel: $PACKAGE_PARALLEL
installed: $PACKAGE_INSTALLED
EOF
    } | yq eval '. | with_entries(select(.value != null))'
}

# __info_the_given_available_package_as_shell <PACKAGE-NAME>
  __info_the_given_available_package_as_shell() {
    __load_formula_of_the_given_package "$1"

    while read KEY
    do
        printf "%s='%s'\n" "${PACKAGE_NAME_UPPERCASE_UNDERSCORE}_${KEY}" "$(eval echo \$$KEY)"
    done <<EOF
PACKAGE_NAME
PACKAGE_PKGTYPE
PACKAGE_SUMMARY
PACKAGE_LICENSE
PACKAGE_VERSION
PACKAGE_VERSION_MAJOR
PACKAGE_VERSION_MINOR
PACKAGE_VERSION_PATCH
PACKAGE_VERSION_TWEAK
PACKAGE_WEB_URL
PACKAGE_GIT_URL
PACKAGE_GIT_SHA
PACKAGE_GIT_REF
PACKAGE_GIT_NTH
PACKAGE_SRC_URL
PACKAGE_SRC_URI
PACKAGE_SRC_SHA
PACKAGE_SRC_FILETYPE
PACKAGE_SRC_FILENAME
PACKAGE_SRC_FILEPATH
PACKAGE_FIX_URL
PACKAGE_FIX_URI
PACKAGE_FIX_SHA
PACKAGE_FIX_FILETYPE
PACKAGE_FIX_FILENAME
PACKAGE_FIX_FILEPATH
PACKAGE_RES_URL
PACKAGE_RES_URI
PACKAGE_RES_SHA
PACKAGE_RES_FILETYPE
PACKAGE_RES_FILENAME
PACKAGE_RES_FILEPATH
PACKAGE_BSCRIPT
PACKAGE_BSYSTEM
PACKAGE_BINBSTD
PACKAGE_USE_BSYSTEM_GO
PACKAGE_USE_BSYSTEM_RAKE
PACKAGE_USE_BSYSTEM_NINJA
PACKAGE_USE_BSYSTEM_GMAKE
PACKAGE_USE_BSYSTEM_CMAKE
PACKAGE_USE_BSYSTEM_XMAKE
PACKAGE_USE_BSYSTEM_MESON
PACKAGE_USE_BSYSTEM_CARGO
PACKAGE_USE_BSYSTEM_AUTOGENSH
PACKAGE_USE_BSYSTEM_AUTOTOOLS
PACKAGE_USE_BSYSTEM_CONFIGURE
PACKAGE_DEP_PKG
PACKAGE_DEP_LIB
PACKAGE_DEP_UPP
PACKAGE_DEP_PYM
PACKAGE_DEP_PLM
PACKAGE_CCFLAGS
PACKAGE_XXFLAGS
PACKAGE_PPFLAGS
PACKAGE_LDFLAGS
PACKAGE_API_MIN
PACKAGE_FORMULA_FILEPATH
PACKAGE_PARALLEL
PACKAGE_DEVELOPER
EOF
}

# __info_the_given_available_package <PACKAGE-NAME> [--yaml] [--json] [<KEY>]
# __info_the_given_available_package curl
# __info_the_given_available_package curl version
# __info_the_given_available_package curl web-url
  __info_the_given_available_package() {
    case $2 in
        --yaml|'')
            __info_the_given_available_package_as_yaml "$1"
            ;;
        --json)
            __info_the_given_available_package_as_json "$1"
            ;;
        onstart)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_ONSTART"
            ;;
        onready)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_ONREADY"
            ;;
        onfinal)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_ONFINAL"
            ;;
        do12345)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_DO12345"
            ;;
        dopatch)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_DOPATCH"
            ;;
        prepare)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_PREPARE"
            ;;
        install)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_DOBUILD"
            ;;
        dotweak)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_DOTWEAK"
            ;;
        caveats)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_CAVEATS"
            ;;
        developer)
            __load_formula_of_the_given_package "$1"
            printf '%s\n' "$PACKAGE_DEVELOPER"
            ;;
        src-ft)
            __load_formula_of_the_given_package "$1"
            if [ -n "$PACKAGE_SRC_FILETYPE" ] ; then
                printf '%s\n' "$PACKAGE_SRC_FILETYPE"
            fi
            ;;
        src-fp)
            __load_formula_of_the_given_package "$1"
            if [ -n "$PACKAGE_SRC_FILEPATH" ] ; then
                printf '%s\n' "$PACKAGE_SRC_FILEPATH"
            fi
            ;;
        fix-ft)
            __load_formula_of_the_given_package "$1"
            if [ -n "$PACKAGE_FIX_FILETYPE" ] ; then
                printf '%s\n' "$PACKAGE_FIX_FILETYPE"
            fi
            ;;
        fix-fp)
            __load_formula_of_the_given_package "$1"
            if [ -n "$PACKAGE_FIX_FILEPATH" ] ; then
                printf '%s\n' "$PACKAGE_FIX_FILEPATH"
            fi
            ;;
        res-ft)
            __load_formula_of_the_given_package "$1"
            if [ -n "$PACKAGE_RES_FILETYPE" ] ; then
                printf '%s\n' "$PACKAGE_RES_FILETYPE"
            fi
            ;;
        res-fp)
            __load_formula_of_the_given_package "$1"
            if [ -n "$PACKAGE_RES_FILEPATH" ] ; then
                printf '%s\n' "$PACKAGE_RES_FILEPATH"
            fi
            ;;
        *)  __load_formula_of_the_given_package "$1"
            __PACKAGE_GET__KEY__="$(printf '%s\n' "$2" | tr '+-.' '_' | tr a-z A-Z)"
            eval echo \$PACKAGE_$__PACKAGE_GET__KEY__
    esac
}

# __info_the_given_installed_package <PACKAGE-SPEC> [<KEY>]
# __info_the_given_installed_package curl
# __info_the_given_installed_package curl version
# __info_the_given_installed_package curl web-url
  __info_the_given_installed_package() {
    case $2 in
        --yaml|'')
            PACKAGE_SPEC=
            PACKAGE_SPEC="$(inspect_package_spec "$1")"

            if is_package_installed "$PACKAGE_SPEC" ; then
                yq "$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC/.ndk-pkg/RECEIPT.yml"
            else
                abort 1 "package '$PACKAGE_SPEC' is not installed."
            fi
            ;;
        --json)
            __info_the_given_installed_package_as_json "$1"
            ;;
        --prefix)
            PACKAGE_SPEC=
            PACKAGE_SPEC="$(inspect_package_spec "$1")"

            if is_package_installed "$PACKAGE_SPEC" ; then
                PACKAGE_INSTALLED_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC"
                printf '%s\n' "$PACKAGE_INSTALLED_DIR"
            else
                abort 1 "package '$PACKAGE_SPEC' is not installed."
            fi
            ;;
        --files)
            PACKAGE_SPEC=
            PACKAGE_SPEC="$(inspect_package_spec "$1")"

            if is_package_installed "$PACKAGE_SPEC" ; then
                cut -d '|' -f3 "$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC/.ndk-pkg/MANIFEST.txt"
            else
                abort 1 "package '$PACKAGE_SPEC' is not installed."
            fi
            ;;
        builtat)
            __load_receipt_of_the_given_package "$1"
            printf '%s\n' "$RECEIPT_PACKAGE_BUILTAT"
            ;;
        builtat-rfc-3339)
            __load_receipt_of_the_given_package "$1"
            date -d "@$RECEIPT_PACKAGE_BUILTAT" '+%Y-%m-%d %H:%M:%S%:z'
            ;;
        builtat-iso-8601)
            __load_receipt_of_the_given_package "$1"
            date -d "@$RECEIPT_PACKAGE_BUILTAT" '+%Y-%m-%dT%H:%M:%S%:z'
            ;;
        builtat-rfc-3339-utc)
            __load_receipt_of_the_given_package "$1"
            date -u -d "@$RECEIPT_PACKAGE_BUILTAT" '+%Y-%m-%d %H:%M:%S%:z'
            ;;
        builtat-iso-8601-utc)
            __load_receipt_of_the_given_package "$1"
            date -u -d "@$RECEIPT_PACKAGE_BUILTAT" '+%Y-%m-%dT%H:%M:%SZ'
            ;;
        *)  __load_receipt_of_the_given_package "$1"
            __PACKAGE_GET__KEY__="$(printf '%s\n' "$2" | tr '+-.' '_' | tr a-z A-Z)"
            eval echo \$RECEIPT_PACKAGE_$__PACKAGE_GET__KEY__
    esac
}

# __info_the_given_installed_package_as_json <PACKAGE-NAME|PACKAGE_SPEC>
  __info_the_given_installed_package_as_json() {
    __load_receipt_of_the_given_package "$1"

    jq  --null-input \
        --arg pkgname "$RECEIPT_PACKAGE_PKGNAME" \
        --arg pkgtype "$RECEIPT_PACKAGE_PKGTYPE" \
        --arg version "$RECEIPT_PACKAGE_VERSION" \
        --arg summary "$RECEIPT_PACKAGE_SUMMARY" \
        --arg license "$RECEIPT_PACKAGE_LICENSE" \
        --arg web_url "$RECEIPT_PACKAGE_WEB_URL" \
        --arg git_url "$RECEIPT_PACKAGE_GIT_URL" \
        --arg git_sha "$RECEIPT_PACKAGE_GIT_SHA" \
        --arg git_ref "$RECEIPT_PACKAGE_GIT_REF" \
        --arg git_nth "$RECEIPT_PACKAGE_GIT_NTH" \
        --arg src_url "$RECEIPT_PACKAGE_SRC_URL" \
        --arg src_uri "$RECEIPT_PACKAGE_SRC_URI" \
        --arg src_sha "$RECEIPT_PACKAGE_SRC_SHA" \
        --arg fix_url "$RECEIPT_PACKAGE_FIX_URL" \
        --arg fix_uri "$RECEIPT_PACKAGE_FIX_URI" \
        --arg fix_sha "$RECEIPT_PACKAGE_FIX_SHA" \
        --arg res_url "$RECEIPT_PACKAGE_RES_URL" \
        --arg res_uri "$RECEIPT_PACKAGE_RES_URI" \
        --arg res_sha "$RECEIPT_PACKAGE_RES_SHA" \
        --arg patches "$RECEIPT_PACKAGE_PATCHES" \
        --arg reslist "$RECEIPT_PACKAGE_RESLIST" \
        --arg dep_pkg "$RECEIPT_PACKAGE_DEP_PKG" \
        --arg dep_lib "$RECEIPT_PACKAGE_DEP_LIB" \
        --arg dep_upp "$RECEIPT_PACKAGE_DEP_UPP" \
        --arg dep_pym "$RECEIPT_PACKAGE_DEP_PYM" \
        --arg dep_plm "$RECEIPT_PACKAGE_DEP_PLM" \
        --arg onstart "$RECEIPT_PACKAGE_ONSTART" \
        --arg onready "$RECEIPT_PACKAGE_ONREADY" \
        --arg onfinal "$RECEIPT_PACKAGE_ONFINAL" \
        --arg do12345 "$RECEIPT_PACKAGE_DO12345" \
        --arg dopatch "$RECEIPT_PACKAGE_DOPATCH" \
        --arg prepare "$RECEIPT_PACKAGE_PREPARE" \
        --arg install "$RECEIPT_PACKAGE_DOBUILD" \
        --arg dotweak "$RECEIPT_PACKAGE_DOTWEAK" \
        --arg caveats "$RECEIPT_PACKAGE_CAVEATS" \
        --arg bsystem "$RECEIPT_PACKAGE_BSYSTEM" \
        --arg binbstd "$RECEIPT_PACKAGE_BINBSTD" \
        --arg ccflags "$RECEIPT_PACKAGE_CCFLAGS" \
        --arg xxflags "$RECEIPT_PACKAGE_XXFLAGS" \
        --arg ppflags "$RECEIPT_PACKAGE_PPFLAGS" \
        --arg ldflags "$RECEIPT_PACKAGE_LDFLAGS" \
        --arg parallel $RECEIPT_PACKAGE_PARALLEL \
        --arg developer "$RECEIPT_PACKAGE_DEVELOPER" \
        --arg builtby "$RECEIPT_PACKAGE_BUILTBY" \
        --arg builtat "$RECEIPT_PACKAGE_BUILTAT" \
        --arg builtfor "$RECEIPT_PACKAGE_BUILTFOR" \
'{
    "pkgname":$pkgname,
    "pkgtype":$pkgtype,
    "version":$version,
    "license":$license,
    "summary":$summary,
    "web-url":$web_url,
    "git-url":$git_url,
    "git-sha":$git_sha,
    "git-ref":$git_ref,
    "git-nth":$git_nth,
    "src-url":$src_url,
    "src-uri":$src_uri,
    "src-sha":$src_sha,
    "fix-url":$fix_url,
    "fix-uri":$fix_uri,
    "fix-sha":$fix_sha,
    "res-url":$res_url,
    "res-uri":$res_uri,
    "res-sha":$res_sha,
    "patches":$patches,
    "reslist":$reslist,
    "dep-pkg":$dep_pkg,
    "dep-lib":$dep_lib,
    "dep-upp":$dep_upp,
    "dep-pym":$dep_pym,
    "dep-plm":$dep_plm,
    "bsystem":$bsystem,
    "binbstd":$binbstd,
    "ccflags":$ccflags,
    "xxflags":$xxflags,
    "ppflags":$ppflags,
    "ldflags":$ldflags,
    "parallel":$parallel,
    "developer":$developer,
    "onstart":$onstart,
    "onready":$onready,
    "onfinal":$onfinal,
    "do12345":$do12345,
    "dopatch":$dopatch,
    "prepare":$prepare,
    "install":$install,
    "dotweak":$dotweak,
    "caveats":$caveats,
    "builtby":$builtby,
    "builtat":$builtat,
    "builtfor":$builtfor
}' | jq 'with_entries(select(.value != ""))'
}

# }}}
##############################################################################
# {{{ ndk-pkg fetch

__fetch_resources_of_the_given_package() {
    if [ -z "$1" ] ; then
        abort 1 "__fetch_resources_of_the_given_package <PACKAGE-NAME>, <PACKAGE-NAME> is unspecified."
    fi

    __load_formula_of_the_given_package "$1"

    if [ -n "$PACKAGE_SRC_URL" ] ; then
        case $PACKAGE_SRC_URL in
            dir://*)
                note "$PACKAGE_SRC_URL is local path, no need to fetch."
                return 0
                ;;
            file://*)
                note "$PACKAGE_SRC_URL is local path, no need to fetch."
                return 0
                ;;
            *)  wfetch "$PACKAGE_SRC_URL" --uri="$PACKAGE_SRC_URI" --sha256="$PACKAGE_SRC_SHA" -o "$PACKAGE_SRC_FILEPATH"
        esac
    elif [ -n "$PACKAGE_GIT_URL" ] ; then
        unset GIT_FETCH_URL

        if [ -z "$NDKPKG_URL_TRANSFORM" ] ; then
            GIT_FETCH_URL="$PACKAGE_GIT_URL"
        else
            GIT_FETCH_URL="$("$NDKPKG_URL_TRANSFORM" "$PACKAGE_GIT_URL")" || return 1
        fi

        if [ -z "$PACKAGE_GIT_SHA" ] ; then
            if [ -z "$PACKAGE_GIT_REF" ] ; then
                GIT_BRANCH_NAME=master
                GIT_REF_SPEC="+HEAD:refs/remotes/origin/master"
            else
                GIT_BRANCH_NAME="$(basename "$PACKAGE_GIT_REF")"
                GIT_REF_SPEC="+$PACKAGE_GIT_REF:refs/remotes/origin/$GIT_BRANCH_NAME"
            fi
        else
            GIT_BRANCH_NAME=master
            GIT_REF_SPEC="+$PACKAGE_GIT_SHA:refs/remotes/origin/master"
        fi

        if [ -z "$PACKAGE_GIT_NTH" ] ; then
            PACKAGE_GIT_NTH=1
        fi

        if [ "$PACKAGE_GIT_NTH" -eq 0 ] ; then
            if [ -f "$PACKAGE_SRC_FILEPATH/.git/shallow" ] ; then
                GIT_FETCH_EXTRA_OPTIONS='--unshallow'
            else
                GIT_FETCH_EXTRA_OPTIONS=
            fi
        else
            GIT_FETCH_EXTRA_OPTIONS="--depth=$PACKAGE_GIT_NTH"
        fi

        SESSION_DIR="$NDKPKG_HOME/run/$$"

        run rm -rf     "$SESSION_DIR"
        run install -d "$SESSION_DIR"
        run cd         "$SESSION_DIR"

        run git -c init.defaultBranch=master init
        run git remote add origin "$GIT_FETCH_URL"
        run git -c protocol.version=2 fetch --progress $GIT_FETCH_EXTRA_OPTIONS origin "$GIT_REF_SPEC"
        run git checkout --progress --force -B "$GIT_BRANCH_NAME" "refs/remotes/origin/$GIT_BRANCH_NAME"

        git_submodule_update_recursive

        rm -rf "$SESSION_DIR"
    fi

    if [ -n    "$PACKAGE_FIX_URL" ] ; then
        wfetch "$PACKAGE_FIX_URL" --uri="$PACKAGE_FIX_URI" --sha256="$PACKAGE_FIX_SHA" -o "$PACKAGE_FIX_FILEPATH"
    fi

    if [ -n    "$PACKAGE_RES_URL" ] ; then
        wfetch "$PACKAGE_RES_URL" --uri="$PACKAGE_RES_URI" --sha256="$PACKAGE_RES_SHA" -o "$PACKAGE_RES_FILEPATH"
    fi
}

# }}}
##############################################################################
# {{{ ndk-pkg tree

# __tree_the_given_installed_package <PACKAGE-SPEC>
  __tree_the_given_installed_package() {
    PACKAGE_SPEC=
    PACKAGE_SPEC="$(inspect_package_spec "$1")"

    if is_package_installed "$PACKAGE_SPEC" ; then
        PACKAGE_INSTALLED_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC"
    else
        abort 1 "package '$PACKAGE_SPEC' is not installed."
    fi

    shift

    run tree "$@" "$PACKAGE_INSTALLED_DIR"
}

# }}}
##############################################################################
# {{{ ndk-pkg logs

# __logs_the_given_installed_package <PACKAGE-SPEC>
  __logs_the_given_installed_package() {
    PACKAGE_SPEC="$(inspect_package_spec "$1")"

    if is_package_installed "$PACKAGE_SPEC" ; then
        PACKAGE_INSTALLED_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC"
    else
        abort 1 "package '$PACKAGE_SPEC' is not installed."
    fi

    cd "$PACKAGE_INSTALLED_DIR/.ndk-pkg"

    fzf --preview='bat --color=always --theme=Dracula {}' --preview-window='right:75%'
}

# }}}
##############################################################################
# {{{ ndk-pkg pack

# examples:
# __pack_the_given_installed_package <PACKAGE-NAME> [-t <zip|tar.gz|tar.xz|tar.lz|tar.bz2>] [-o <OUTPUT-PATH>] [--exclude <PATH>] [-K]
# __pack_the_given_installed_package    autoconf     -t tar.xz
  __pack_the_given_installed_package() {
    __load_receipt_of_the_given_package "$1"

    shift

    ###########################################################################################

    unset OUTPUT_PATH
    unset OUTPUT_TYPE

    unset EXCLUDES

    unset REQUEST_TO_KEEP_SESSION_DIR

    while [ -n "$1" ]
    do
        case $1 in
            -K) REQUEST_TO_KEEP_SESSION_DIR=1
                ;;
            -t) shift

                if [ -z "$1" ] ; then
                    abort 1 "$NDKPKG_ARG0 pack <PACKAGE-NAME> [-t <OUTPUT-TYPE>] [-o <OUTPUT-PATH>], -t option is specified but <OUTPUT-TYPE> is unspecified."
                else
                    OUTPUT_TYPE="$1"
                fi

                case $OUTPUT_TYPE in
                    zip|tar.gz|tar.xz|tar.lz|tar.bz2) ;;
                    *) abort 1 "$NDKPKG_ARG0 pack <PACKAGE-NAME> [-t <OUTPUT-TYPE>], unsupported <OUTPU-TYPE>: $OUTPUT_TYPE, <OUTPU-TYPE> should be one of zip|tar.gz|tar.xz|tar.lz|tar.bz2"
                esac
                ;;
            -o) shift
                if [ -z "$1" ] ; then
                    abort 1 "$NDKPKG_ARG0 pack <PACKAGE-NAME> [-t <OUTPU-TYPE>] [-o <OUTPUT-PATH>], -o option is specified but <OUTPUT-PATH> is unspecified."
                else
                    OUTPUT_PATH="$1"
                fi
                ;;
            --exclude)
                shift
                if [ -z "$1" ] ; then
                    abort 1 "$NDKPKG_ARG0 pack <PACKAGE-NAME> [--exclude <PATH>], --exclude option is specified but <PATH> is unspecified."
                else
                    EXCLUDES="$EXCLUDES $1"
                fi
                ;;
            *)  abort 1 "$NDKPKG_ARG0 pack <PACKAGE-NAME> [-t <OUTPUT-TYPE>] [-o <OUTPUT-PATH>] [-K], unrecognized option: $1"
        esac
        shift
    done

    ###########################################################################################

    SESSION_DIR="$NDKPKG_HOME/run/$$"

    run rm -rf     "$SESSION_DIR"
    run install -d "$SESSION_DIR"
    run cd         "$SESSION_DIR"

    if [ -z "$EXCLUDES" ] ; then
        run ln -s "$PACKAGE_INSTALLED_DIR" packed
    else
        run install -d packed

        run cp -r "$PACKAGE_INSTALLED_DIR/." packed/

        for EXCLUDE in $EXCLUDES
        do
            run rm -rf "packed/$EXCLUDE"
        done
    fi

    ###########################################################################################

    unset OUTPUT_FILENAME_NEED_CONTAIN_LIBC_NAME

    if [ -d packed/lib ] ; then
        for FILEPATH in $(find packed/lib -type f)
        do
            case $FILEPATH in
                */lib*.a)   OUTPUT_FILENAME_NEED_CONTAIN_LIBC_NAME=1; break ;;
                */lib*.so*) OUTPUT_FILENAME_NEED_CONTAIN_LIBC_NAME=1; break ;;
                */*.o)      OUTPUT_FILENAME_NEED_CONTAIN_LIBC_NAME=1; break ;;
            esac
        done
    fi

    if [ "$OUTPUT_FILENAME_NEED_CONTAIN_LIBC_NAME" != 1 ] ; then
        if [ -d packed/bin ] ; then
            for FILEPATH in $(find packed/bin -type f)
            do
                FILEMAGIC="$(xxd -u -p -l 4 "$FILEPATH")"

                if [ "$FILEMAGIC" = '7F454C46' ] ; then
                    # http://www.sco.com/developers/gabi/latest/ch4.eheader.html
                    PT_INTERP="$(patchelf --print-interpreter "$FILEPATH" 2>/dev/null || true)"

                    if [ -n "$PT_INTERP" ] ; then
                        OUTPUT_FILENAME_NEED_CONTAIN_LIBC_NAME=1
                        break
                    fi
                fi
            done
        fi
    fi

    if [ "$OUTPUT_FILENAME_NEED_CONTAIN_LIBC_NAME" != 1 ] ; then
        for FILEPATH in $(find packed -type f -name '*.a' -print -quit)
        do
            OUTPUT_FILENAME_NEED_CONTAIN_LIBC_NAME=1
        done
    fi

    if [ "$OUTPUT_FILENAME_NEED_CONTAIN_LIBC_NAME" = 1 ] ; then
        PACKED_DIR_NAME="${RECEIPT_PACKAGE_PKGNAME%@*}-$RECEIPT_PACKAGE_VERSION-$RECEIPT_PACKAGE_BUILTFOR_PLATFORM"
    else
        PACKED_DIR_NAME="${RECEIPT_PACKAGE_PKGNAME%@*}-$RECEIPT_PACKAGE_VERSION-$RECEIPT_PACKAGE_BUILTFOR_PLATFORM_NAME-$RECEIPT_PACKAGE_BUILTFOR_PLATFORM_ARCH"
    fi

    PACKED_DIR_NAME="$PACKED_DIR_NAME.$RECEIPT_PACKAGE_PROFILE"

    ###########################################################################################

    if [ -z "$OUTPUT_TYPE" ] ; then
        case $OUTPUT_PATH in
            *.tar.gz|*.tgz)
                OUTPUT_TYPE=tar.gz
                ;;
            *.tar.xz|*.txz)
                OUTPUT_TYPE=tar.xz
                ;;
            *.tar.lz|*.tlz)
                OUTPUT_TYPE=tar.lz
                ;;
            *.tar.bz2|*.tbz2)
                OUTPUT_TYPE=tar.bz2
                ;;
            *.zip)
                OUTPUT_TYPE=zip
                ;;
            *.7z)
                OUTPUT_TYPE=7z
                ;;
            *)  OUTPUT_TYPE=tar.xz
        esac
    fi

    ###########################################################################################

    OUTPUT_FILENAME="$PACKED_DIR_NAME.$OUTPUT_TYPE"

    run mv packed "$PACKED_DIR_NAME"

    run bsdtar cvaf "$OUTPUT_FILENAME" "$PACKED_DIR_NAME/*" "$PACKED_DIR_NAME/.ndk-pkg"

    run du -sh "$OUTPUT_FILENAME"

    run cd -

    ###########################################################################################

    case $OUTPUT_PATH in
        '') OUTPUT_PATH=.
            ;;
        */*)
            OUTPUT_DIR="${OUTPUT_PATH%/*}"

            [ -d "$OUTPUT_DIR" ] || run install -d "$OUTPUT_DIR"
    esac

    ###########################################################################################

    run mv "$SESSION_DIR/$OUTPUT_FILENAME" "$OUTPUT_PATH"

    ###########################################################################################

    if [ "$REQUEST_TO_KEEP_SESSION_DIR" = 1 ] ; then
        echo
        note "the session directory '$SESSION_DIR' is not deleted as -K option is specified."
    else
        run rm -rf "$SESSION_DIR"
    fi
}

# https://developer.android.com/studio/projects/android-library#aar-contents
# https://google.github.io/prefab/#package-structure
#
# libz-1.2.13.aar
# ├── AndroidManifest.xml
# ├── META-INF
# │   ├── FAQ
# │   ├── LICENSE
# │   ├── MANIFEST.txt
# │   ├── README
# │   ├── README.ndk-pkg
# │   └── RECEIPT.yml
# └── prefab
#     ├── prefab.json
#     └── modules
#         ├── headers
#         │   └── include
#         │       ├── zconf.h
#         │       └── zlib.h
#         ├── libz.a
#         │   ├── module.josn
#         │   ├── include
#         │   │   ├── zconf.h
#         │   │   └── zlib.h
#         │   └── libs
#         │       ├── android.arm64-v8a
#         │       │   ├── abi.json
#         │       │   └── libz.a
#         │       ├── android.armeabi-v7a
#         │       │   ├── abi.json
#         │       │   └── libz.a
#         │       ├── android.x86
#         │       │   ├── abi.json
#         │       │   └── libz.a
#         │       └── android.x86_64
#         │           ├── abi.json
#         │           └── libz.a
#         └── libz.so
#             ├── module.josn
#             ├── include
#             │   ├── zconf.h
#             │   └── zlib.h
#             └── libs
#                 ├── android.arm64-v8a
#                 │   ├── abi.json
#                 │   └── libz.so
#                 ├── android.armeabi-v7a
#                 │   ├── abi.json
#                 │   └── libz.so
#                 ├── android.x86
#                 │   ├── abi.json
#                 │   └── libz.so
#                 └── android.x86_64
#                     ├── abi.json
#                     └── libz.so
#
# __export_google_prefab_aar_for_the_given_installed_packages android-<ANDROID-API>-<ANDROID-ABIs>/<PACKAGE-NAME> [--ndk-home=<ANDROID-NDK-HOME>] [-o <OUTPUT-PATH>] [-K]
  __export_google_prefab_aar_for_the_given_installed_packages() {
    unset PACKAGE_NAME

    unset TARGET_PLATFORM_VERS
    unset TARGET_PLATFORM_ABIS

    case $1 in
        android-[1-9][0-9]-*/*)
            PACKAGE_NAME="${1#*/}"

            if [ -z "$PACKAGE_NAME" ] ; then
                abort 1 "invalid package spec: $1\n    package name is unspecifed."
            elif printf '%s\n' "$PACKAGE_NAME" | grep -q -E '^[A-Za-z0-9+-_.@]{1,50}$' ; then
                :
            else
                abort 1 "invalid package spec: $1\n    package name must match the regular expression pattern: ^[A-Za-z0-9+-_.@]{1,50}$"
            fi

            TARGET_PLATFORM_SPEC="${1%%/*}"
            TARGET_PLATFORM_VERS="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -d- -f2)"
            TARGET_PLATFORM_ABIS="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -c 12- | tr ',' ' ')"

            for TARGET_PLATFORM_ABI in $TARGET_PLATFORM_ABIS
            do
                case $TARGET_PLATFORM_ABI in
                    armeabi-v7a|arm64-v8a|x86|x86_64)
                        ;;
                    *)  abort 1 "invalid package spec: $1\n    unsupported abi: $TARGET_PLATFORM_ABI"
                esac
            done
            ;;
        *)  abort 1 "invalid package spec: $1"
    esac

    #########################################################################################

    shift

    unset ANDROID_NDK_HOME

    unset OUTPUT_PATH

    unset REQUEST_TO_KEEP_SESSION_DIR

    while [ -n "$1" ]
    do
        case $1 in
            --ndk-home=*)
                ANDROID_NDK_HOME="${1#*=}"
                ;;
            -o) shift
                OUTPUT_PATH="$1"
                ;;
            -K) REQUEST_TO_KEEP_SESSION_DIR=1
                ;;
        esac
        shift
    done

    #########################################################################################

    PACKAGE_SPEC="android-$TARGET_PLATFORM_VERS-$TARGET_PLATFORM_ABI/$PACKAGE_NAME"

    __load_receipt_of_the_given_package "$PACKAGE_SPEC"

    #########################################################################################

    setup_android_ndk_env "$ANDROID_NDK_HOME"

    #########################################################################################

    SESSION_DIR="$NDKPKG_HOME/run/$$"

    step "create the session directory and change to it"
    run rm -rf     "$SESSION_DIR"
    run install -d "$SESSION_DIR"
    run cd         "$SESSION_DIR"

    #########################################################################################

    PACKAGE_INCLUDE_DIR="$PACKAGE_INSTALLED_DIR/include"

    if [ ! -d "$PACKAGE_INCLUDE_DIR" ] ; then
        abort 10 "unable to be exported as google prefab due to there is no C/C++ header files in package $PACKAGE_SPEC"
    fi

    #########################################################################################

    # For compatibility with CMake, versions must be specified as major[.minor[.patch[.tweak]]] with all components being numeric.

    if [ -z "$RECEIPT_PACKAGE_VERSION" ] ; then
        PREFAB_PACKAGE_VERS="$(date -u -d "@$RECEIPT_PACKAGE_BUILTAT" '+%Y.%m.%d')"
    else
        unset SED_E

        I=0

        for CHAR in a b c d e f g h i j k l m n o p q r s t u v w x y z
        do
            I=$(expr "$I" + 1)
            SED_E="$SED_E -e s|$CHAR|.$I|g"
        done

        # r3060
        # 1.4.rc5
        # 2022-03-28
        # 2.4+20151223
        # 1.1.1n
        # 9e
        # 2.8.9rel.1
        # 0.14.1-beta
        # 0.99.beta20
        # 3.0-11
        # 1.4g
        # 0.15.2e
        PREFAB_PACKAGE_VERS="$(printf '%s\n' "$RECEIPT_PACKAGE_VERSION" | tr +- . | sed -e 's|beta|2|' -e 's|rel||' -e 's|rc|.|' -e 's|^r||' $SED_E)"

        printf '%s\n' "$PREFAB_PACKAGE_VERS" | {
            grep -q '[0-9]*' ||
            grep -q '[0-9]*\.[0-9]*' ||
            grep -q '[0-9]*\.[0-9]*\.[0-9]*' ||
            grep -q '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*'
        } || abort 1 "version[$PREFAB_PACKAGE_VERS] not expected."
    fi

    #########################################################################################

    PREFAB_PACKAGE_NAME="${PACKAGE_NAME%@*}"

    #########################################################################################

    # https://google.github.io/prefab/#package-metadata
    install -d prefab
    cat > prefab/prefab.json <<EOF
{
  "schema_version": 2,
  "name": "$PREFAB_PACKAGE_NAME",
  "version": "$PREFAB_PACKAGE_VERS",
  "dependencies": [ ]
}
EOF

    #########################################################################################

    step "create META-INF"
    run cp -r "$PACKAGE_INSTALLED_DIR/.ndk-pkg" META-INF
    cat > META-INF/README.ndk-pkg <<EOF
packed by ndk-pkg-$NDKPKG_VERSION in $(date -u -d "@$TIMESTAMP_UNIX" '+%Y-%m-%d %H:%M:%SZ')

For more details, please refer to https://github.com/leleliu008/ndk-pkg
EOF

    #########################################################################################

    unset JAVA_PACKAGE_NAME

    # java package name characters [a-z0-9_.]
    JAVA_PACKAGE_NAME="$(printf '%s\n' "$PREFAB_PACKAGE_NAME" | tr '+-.' '_' | tr A-Z a-z)"

    step "create AndroidManifest.xml"
    tee AndroidManifest.xml <<EOF
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.fpliu.ndk.pkg.prefab.android$TARGET_PLATFORM_VERS.$JAVA_PACKAGE_NAME">

    <uses-sdk android:minSdkVersion="$TARGET_PLATFORM_VERS" />

</manifest>
EOF

    #########################################################################################

    step "create the header-only module"
    run install -d prefab/modules/headers
    run cp -r "$PACKAGE_INCLUDE_DIR" prefab/modules/headers/

    #########################################################################################

    PACKAGE_LIBRARY_DIR="$PACKAGE_INSTALLED_DIR/lib"

    unset LIBRARIes

    if [ -d "$PACKAGE_LIBRARY_DIR" ] ; then
        LIBRARIes="$(find "$PACKAGE_LIBRARY_DIR" -maxdepth 1 -mindepth 1 \( -type f -or -type l \) \( -name 'lib*.a' -or -name 'lib*.so' \) -printf '%f:%l\n')"
    fi

    for LIBRARY in $LIBRARIes
    do
        LIBRARY_FILENAME="${LIBRARY%:*}"

        LIBRARY_REALNAME="${LIBRARY#*:}"

        if [ -z "$LIBRARY_REALNAME" ] ; then
            LIBRARY_REALNAME="$LIBRARY_FILENAME"
        fi

        case $LIBRARY_FILENAME in
            *.a)  LIBRARY_FILENAME_PREFIX="${LIBRARY_FILENAME%.a}"  ; IS_STATIC_LIBRARY=true  ;;
            *.so) LIBRARY_FILENAME_PREFIX="${LIBRARY_FILENAME%.so}" ; IS_STATIC_LIBRARY=false ;;
        esac

        MODULE_DIR="prefab/modules/$LIBRARY_FILENAME"

        step "create module : $LIBRARY_FILENAME"

        run install -d "$MODULE_DIR"

        cat > "$MODULE_DIR/module.json" <<EOF
{
  "export_libraries": [],
  "android": {
    "library_name": "$LIBRARY_FILENAME_PREFIX",
    "export_libraries": []
  }
}
EOF

        for TARGET_PLATFORM_ABI in $TARGET_PLATFORM_ABIS
        do
            PACKAGE_SPEC="android-$TARGET_PLATFORM_VERS-$TARGET_PLATFORM_ABI/$PACKAGE_NAME"

            __load_receipt_of_the_given_package "$PACKAGE_SPEC"

            PACKAGE_INCLUDE_DIR="$PACKAGE_INSTALLED_DIR/include"
            PACKAGE_LIBRARY_DIR="$PACKAGE_INSTALLED_DIR/lib"

            LIBRARY_FILEPATH="$PACKAGE_LIBRARY_DIR/$LIBRARY_REALNAME"

            #################################################################

            ABI_DIR="$MODULE_DIR/libs/android.$TARGET_PLATFORM_ABI"

            run install -d "$ABI_DIR"

            run cp -L -r "$PACKAGE_INCLUDE_DIR" "$ABI_DIR/"

            run cp -L "$LIBRARY_FILEPATH" "$ABI_DIR/$LIBRARY_FILENAME"

            #################################################################

            STL=none

            # https://github.com/google/prefab/blob/master/api/src/main/kotlin/com/google/prefab/api/Android.kt#L202
            if [ "$IS_STATIC_LIBRARY" = true ] ; then
                if   "$ANDROID_NDK_NM" "$LIBRARY_FILEPATH" | grep -q 'T __cxa_' ; then
                    abort 1 "libc++_static.a has been merged into $LIBRARY_FILEPATH, this may case problems. For more details, please refer to https://developer.android.com/ndk/guides/cpp-support"
                elif "$ANDROID_NDK_NM" "$LIBRARY_FILEPATH" | grep -q 'U __cxa_' ; then
                    STL=c++_shared
                fi
            else
                # http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#dynamic_section
                DT_SONAME="$(patchelf --print-soname "$LIBRARY_FILEPATH")"

                # On Android, a so file must have the DT_SONAME
                # https://github.com/android/ndk/issues/1865
                # https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md
                if [ -z "$DT_SONAME" ] ; then
                    abort 1 "unexpected so file: $LIBRARY_FILEPATH\n    DT_SONAME: not set"
                fi

                if [ "$DT_SONAME" != "$LIBRARY_FILENAME" ] ; then
                    abort 1 "unexpected so file: $LIBRARY_FILEPATH\n    DT_SONAME: $DT_SONAME"
                fi

                DT_NEEDED_LIST="$(patchelf --print-needed "$LIBRARY_FILEPATH")"

                for DT_NEEDED in $DT_NEEDED_LIST
                do
                    case $DT_NEEDED in
                        # besure DT_NEEDED entries all are filenames, not filepaths
                        lib*.so)
                            ;;
                        *)  abort 1 "unexpected so file: $LIBRARY_FILEPATH\n    invalid DT_NEEDED: $DT_NEEDED"
                    esac
                done

                if "$ANDROID_NDK_READELF" -d "$LIBRARY_FILEPATH" | grep -q 'libc++_shared\.so' ; then
                    STL=c++_shared
                elif "$ANDROID_NDK_NM" -D "$LIBRARY_FILEPATH" | grep -q 'T __cxa_' ; then
                    # https://softwareengineering.stackexchange.com/questions/262195/whats-wrong-with-statically-linking-the-stl-into-multiple-shared-libraries
                    # https://developer.android.com/ndk/guides/cpp-support
                    abort 1 "libc++_static.a has been linked into $LIBRARY_FILEPATH, this may case problems. For more details, please refer to https://developer.android.com/ndk/guides/cpp-support"
                fi
            fi

            printf "{\"ndk\": %s, \"api\": %s, \"abi\": \"%s\", \"stl\": \"%s\", \"static\": %s}" "${RECEIPT_PACKAGE_NDKVERS%%.*}" "$TARGET_PLATFORM_VERS" "$TARGET_PLATFORM_ABI" "$STL" "$IS_STATIC_LIBRARY" > "$ABI_DIR/abi.json"

        done
    done

    #########################################################################################

    step "packing the prefab aar file"
    run zip -9 -r prefab.aar .

    step "show size of the prefab aar file"
    run du -sh prefab.aar

    ###########################################################################################

    if [ "$NDKPKG_ARG1" = deploy ] ; then
        return 0
    fi

    if [ -z "$OUTPUT_PATH" ] ; then
        printf '%b\n' "
${COLOR_RED}NOT proceed due to -o <OUTPUT-PATH> option is unspecified.${COLOR_OFF}

${COLOR_RED}If you want to continue, please manually execute following commands:${COLOR_OFF}

cd $SESSION_DIR
mv prefab.aar /somewhere
cd -
rm -rf $SESSION_DIR\n"
        return 0
    fi

    ###########################################################################################

    OUTPUT_DIR="$(dirname "$OUTPUT_PATH")"

    if [ ! -d "$OUTPUT_DIR" ] ; then
        step "create the output directory"
        run install -d "$OUTPUT_DIR"
    fi

    ###########################################################################################

    step "rename prefab aar"
    DEFAULT_OUTPUT_FILENAME="$PREFAB_PACKAGE_NAME-$RECEIPT_PACKAGE_VERSION-android-$TARGET_PLATFORM_VERS-$(printf '%s\n' "$TARGET_PLATFORM_ABIS" | tr ' ' '+').aar"
    run mv prefab.aar "$DEFAULT_OUTPUT_FILENAME"

    ###########################################################################################

    step "back to origin directory"
    run cd -

    ###########################################################################################

    step "move prefab aar file to destination"
    run mv "$SESSION_DIR/$DEFAULT_OUTPUT_FILENAME" "$OUTPUT_PATH"

    if [ "$REQUEST_TO_KEEP_SESSION_DIR" = 1 ] ; then
        echo
        note "the session directory '$SESSION_DIR' is not deleted as -K option is specified."
    else
        step "delete the session directory"
        run rm -rf "$SESSION_DIR"
    fi
}

# __deploy_google_prefab_aar_for_the_given_installed_packages android-<ANDROID-API>/<ANDROID-ABIs>/<PACKAGE-NAME> [--ndk-home=<ANDROID-NDK-HOME>] [--groupId] [-K] [--dry-run] [--debug] [--remote < REMOTE-CONFIG-FILE]
  __deploy_google_prefab_aar_for_the_given_installed_packages() {
    __export_google_prefab_aar_for_the_given_installed_packages "$@"

    #########################################################################################

    shift

    unset DEPLOYED_TO_SONATYPE_OSSRH
    unset DEPLOYED_TO_LOCAL_PATH

    unset MVN_ARGS

    unset DRYRUN

    unset GROUPID

    while [ -n "$1" ]
    do
        case $1 in
            --ndk-home=*)
                ;;
            --debug)
                MVN_ARGS=-X
                ;;
            --remote)
                DEPLOYED_TO_SONATYPE_OSSRH=1
                ;;
            --local=*)
                DEPLOYED_TO_LOCAL_PATH="${1#*=}"
                if [ -n "$DEPLOYED_TO_LOCAL_PATH" ] ; then
                    cd -
                    DEPLOYED_TO_LOCAL_PATH="$(realpath -s "$DEPLOYED_TO_LOCAL_PATH")"
                    cd -
                fi
                ;;
            --dry-run)
                DRYRUN=1
                ;;
            -K) ;;
            --groupId=*)
                GROUPID="${1#*=}"
                ;;
            *)  abort 1 "unrecognized argument: $1"
        esac

        shift
    done

    #########################################################################################

    # https://central.sonatype.org/publish/release/
    # https://central.sonatype.org/publish/requirements/
    # https://central.sonatype.org/publish/publish-manual/

    if [ "$DEPLOYED_TO_SONATYPE_OSSRH" = 1 ] ; then
        if [ -z "$RECEIPT_PACKAGE_LICENSE" ] ; then
            abort 1 "can not be deployed to Sonatype OSSRH due to RECEIPT_PACKAGE_LICENSE is empty."
        fi

        if [ -z "$RECEIPT_PACKAGE_GIT_URL" ] ; then
            abort 1 "can not be deployed to Sonatype OSSRH due to RECEIPT_PACKAGE_GIT_URL is empty."
        fi

        if [ -n "$DEPLOYED_TO_LOCAL_PATH" ] ; then
            abort 1 '--remote and --local=<DIR> can not be used as the same time.'
        fi
    fi

    #########################################################################################

    [ -z "$GROUPID" ] && GROUPID="com.fpliu.ndk.pkg.prefab.android.$TARGET_PLATFORM_VERS"

    #########################################################################################

    step "generating POM file"

    cat > pom.xml <<EOF
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>$GROUPID</groupId>
    <artifactId>$PREFAB_PACKAGE_NAME</artifactId>
    <version>$RECEIPT_PACKAGE_VERSION</version>
    <packaging>aar</packaging>

    <name>$PREFAB_PACKAGE_NAME</name>
    <description>$RECEIPT_PACKAGE_SUMMARY</description>
    <url>$RECEIPT_PACKAGE_WEB_URL</url>
EOF

    RECEIPT_PACKAGE_SCM_URL=

    if [ -n "$RECEIPT_PACKAGE_GIT_URL" ] ; then
        RECEIPT_PACKAGE_SCM_TYPE=git
        RECEIPT_PACKAGE_SCM_URL="$RECEIPT_PACKAGE_GIT_URL"
    fi

    if [ -n "$RECEIPT_PACKAGE_SCM_URL" ] ; then
        cat >> pom.xml <<EOF

    <scm>
        <connection>scm:$RECEIPT_PACKAGE_SCM_TYPE:$RECEIPT_PACKAGE_SCM_URL</connection>
        <developerConnection>scm:$RECEIPT_PACKAGE_SCM_TYPE:$RECEIPT_PACKAGE_SCM_URL</developerConnection>
        <url>$RECEIPT_PACKAGE_SCM_URL</url>
    </scm>
EOF
    fi

    if [ -z "$RECEIPT_PACKAGE_DEVELOPER" ] ; then
        RECEIPT_PACKAGE_DEVELOPER="$PREFAB_PACKAGE_NAME developers <unknown>"
    fi

    printf '\n    <developers>\n' >> pom.xml

    export IFS='
'

    for DEVELOPER in $RECEIPT_PACKAGE_DEVELOPER
    do
        DEVELOPER_NAME=
        DEVELOPER_EMAIL=

        DEVELOPER_NAME="${DEVELOPER% <*>}"

        if [ "$DEVELOPER" != "$DEVELOPER_NAME" ] ; then
            DEVELOPER_EMAIL="$(printf '%s\n' "$DEVELOPER" | sed -e 's|.* <\(.*\)>|\1|')"
        fi

        cat >> pom.xml <<EOF
        <developer>
            <name>$DEVELOPER_NAME</name>
            <email>$DEVELOPER_EMAIL</email>
        </developer>
EOF
    done

    unset IFS

    printf '    </developers>\n' >> pom.xml

    if [ -n "$RECEIPT_PACKAGE_LICENSE" ] ; then
        printf '\n    <licenses>\n' >> pom.xml

        for LICENSE_NAME in $RECEIPT_PACKAGE_LICENSE
        do
            cat >> pom.xml <<EOF
        <license>
            <name>$LICENSE_NAME</name>
            <url>https://raw.githubusercontent.com/spdx/license-list-data/master/text/$LICENSE_NAME.txt</url>
        </license>
EOF
        done

        printf '    </licenses>\n' >> pom.xml
    fi

    printf '</project>\n' >> pom.xml

    cat pom.xml

    #########################################################################################

    POM_FILE="$PREFAB_PACKAGE_NAME-$RECEIPT_PACKAGE_VERSION.pom"

    # here we must rename pom.xml to another file name, otherwise it will be treated as a maven project.
    mv pom.xml "$POM_FILE"

    #########################################################################################

    if [ "$DEPLOYED_TO_SONATYPE_OSSRH" = 1 ] ; then
        unset SERVER_ID
        unset SERVER_URL
        unset SERVER_USERNAME
        unset SERVER_PASSWORD
        unset GPG_PASSPHRASE

        step "read config from stdin"
        while read -r LINE
        do
            case $LINE in
                SERVER_ID=*)        SERVER_ID="${LINE#*=}" ;;
                SERVER_URL=*)       SERVER_URL="${LINE#*=}" ;;
                SERVER_USERNAME=*)  SERVER_USERNAME="${LINE#*=}" ;;
                SERVER_PASSWORD=*)  SERVER_PASSWORD="${LINE#*=}" ;;
                GPG_PASSPHRASE=*)   GPG_PASSPHRASE="${LINE#*=}" ;;
            esac
        done

        [ -z "$SERVER_ID" ]       && abort 1 "SERVER_ID must not be enpty."
        [ -z "$SERVER_URL" ]      && abort 1 "SERVER_URL must not be enpty."
        [ -z "$SERVER_USERNAME" ] && abort 1 "SERVER_USERNAME must not be enpty."
        [ -z "$SERVER_PASSWORD" ] && abort 1 "SERVER_PASSWORD must not be enpty."
        [ -z "$GPG_PASSPHRASE"  ] && abort 1 "GPG_PASSPHRASE must not be enpty."

        #########################################################################################

        on_exit_deploy() {
            rm -f settings.xml
        }

        trap on_exit_deploy EXIT

        # https://maven.apache.org/plugins/maven-gpg-plugin/usage.html
        step "generating settings.xml"
        cat > settings.xml <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <servers>
    <server>
      <id>$SERVER_ID</id>
      <username>$SERVER_USERNAME</username>
      <password>$SERVER_PASSWORD</password>
    </server>

    <server>
      <id>gpg.passphrase</id>
      <passphrase>$GPG_PASSPHRASE</passphrase>
    </server>
  </servers>
</settings>
EOF

        #########################################################################################

        # There are two maven plugins maven-deploy-plugin and maven-gpg-plugin can be used
        # maven-gpg-plugin is preferred as it is automatically signed with gpg.
        #
        # https://maven.apache.org/plugins/maven-gpg-plugin/sign-and-deploy-file-mojo.html
        # https://maven.apache.org/plugins/maven-deploy-plugin/deploy-file-mojo.html
        # https://maven.apache.org/resolver/configuration.html

        MVN_ARGS="$MVN_ARGS --settings settings.xml gpg:sign-and-deploy-file -Dfile=prefab.aar -DgeneratePom=false -DpomFile=$POM_FILE -Daether.checksums.algorithms=MD5,SHA-1,SHA-256,SHA-512 -DrepositoryId=$SERVER_ID -Durl=$SERVER_URL"
    else
        # https://maven.apache.org/plugins/maven-install-plugin/install-file-mojo.html
        MVN_ARGS="$MVN_ARGS install:install-file -Dfile=prefab.aar -DgroupId=$GROUPID -DartifactId=$PREFAB_PACKAGE_NAME -Dversion=$RECEIPT_PACKAGE_VERSION -Dpackaging=aar -DgeneratePom=false -DpomFile=$POM_FILE"

        if [ -n "$DEPLOYED_TO_LOCAL_PATH" ] ; then
            MVN_ARGS="$MVN_ARGS -DlocalRepositoryPath=$DEPLOYED_TO_LOCAL_PATH"
        fi
    fi

    #########################################################################################

    if [ "$DRYRUN" = 1 ] ; then
        printf '%b\n' "
${COLOR_RED}NOT proceed due to --dry-run option is specified.${COLOR_OFF}

${COLOR_RED}If you want to continue, please manually execute following commands:${COLOR_OFF}

cd $SESSION_DIR
run mvn $MVN_ARGS
cd -
rm -rf $SESSION_DIR\n"
        return 0
    fi

    #########################################################################################

    MVN="$(command -v mvn || true)"

    if [ -z "$MVN" ] ; then
        step install maven
        uppm install maven

        unset  JAVA_HOME
        unset MAVEN_HOME

        export JAVA_HOME="$UPPM_HOME/installed/jdk17"
        export PATH="$JAVA_HOME/bin:$PATH"
        export CLASSPATH=".:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar"

        MAVEN_HOME="$UPPM_HOME/installed/maven"

        MVN="$MAVEN_HOME/bin/mvn"
    fi

    #########################################################################################

    step "deploying"
    run "$MVN $MVN_ARGS"

    #########################################################################################

    PREFAB_MODULES="$(find prefab/modules -maxdepth 1 -mindepth 1 -type d -printf '%f ')"

    #########################################################################################

    if [ "$REQUEST_TO_KEEP_SESSION_DIR" = 1 ] ; then
        printf '\n'
        note "the session directory '$SESSION_DIR' is not deleted as -K option is given."
    else
        step "delete the session directory"
        run rm -rf "$SESSION_DIR"
    fi

    #########################################################################################

    if [ "$DEPLOYED_TO_SONATYPE_OSSRH" = 1 ] ; then
        MAVEN_REPO_FUNC=mavenCentral
    else
        MAVEN_REPO_FUNC=mavenLocal
    fi

    #########################################################################################

    printf '%b\n' "
${COLOR_YELLOW}
======================================================
Use this artifact alongside with Android Gradle Plugin
======================================================${COLOR_OFF}

${COLOR_GREEN}step1. enable prefab feature for Android Gradle Plugin${COLOR_OFF}

android {
    buildFeatures {
        prefab true
    }
}

${COLOR_GREEN}step2. enable $MAVEN_REPO_FUNC repository for Gradle${COLOR_OFF}

repositories {
    $MAVEN_REPO_FUNC()
}

${COLOR_GREEN}step3. configure dependencies${COLOR_OFF}

${COLOR_PURPLE}For Gradle Groovy DSL:${COLOR_OFF}

dependencies {
    implementation '$GROUPID:$PREFAB_PACKAGE_NAME:$RECEIPT_PACKAGE_VERSION@aar'
}

${COLOR_PURPLE}For Gradle Kotlin DSL:${COLOR_OFF}

dependencies {
    implementation(\"$GROUPID:$PREFAB_PACKAGE_NAME:$RECEIPT_PACKAGE_VERSION@aar\")
}

${COLOR_GREEN}step4. find package and link libraries in your Android project's CMakeLists.txt${COLOR_OFF}

${COLOR_PURPLE}This prefab package has $(list_size $PREFAB_MODULES) modules : $PREFAB_MODULES${COLOR_OFF}

find_package($PREFAB_PACKAGE_NAME REQUIRED CONFIG)
target_link_libraries(app $PREFAB_PACKAGE_NAME::${PREFAB_MODULES%% *})

${COLOR_GREEN}step5. configure C++ standard and STL (optional)${COLOR_OFF}

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments '-DANDROID_STL=c++_shared'
                cppFlags  '-std=c++17'
            }
        }
    }
}

${COLOR_RED}Caveat: If you link a shared library that depends on libc++_shared.so, then your Android app should use libc++_shared.so too.${COLOR_OFF}\n"
}

# }}}
##############################################################################
# {{{ ndk-pkg cleanup

__cleanup() {
    success "Done."
}

# }}}
##############################################################################
# {{{ ndk-pkg upgrade-self

# __upgrade_self <URL>
  __upgrade_self() {
    [ -z "$1" ] && abort 1 "__upgrade_self <URL> , <URL> must be non-empty."

    unset CURRENT_SCRIPT_REALPATH

    # if file exists and is a symbolic link
    if [ -L "$NDKPKG_PATH" ] ; then
        # https://unix.stackexchange.com/questions/136494/whats-the-difference-between-realpath-and-readlink-f#:~:text=GNU%20coreutils%20introduced%20a%20realpath,in%20common%20with%20GNU%20readlink%20.
        if command -v realpath > /dev/null ; then
            CURRENT_SCRIPT_REALPATH=$(realpath "$NDKPKG_PATH")
        elif command -v readlink > /dev/null && readlink -f xx > /dev/null 2>&1 ; then
            CURRENT_SCRIPT_REALPATH=$(readlink -f "$NDKPKG_PATH")
        else
            CURRENT_SCRIPT_REALPATH=$(realpath "$NDKPKG_PATH")
        fi
    else
        CURRENT_SCRIPT_REALPATH="$NDKPKG_PATH"
    fi

    SESSION_DIR="$NDKPKG_HOME/run/$$"

    run rm -rf     "$SESSION_DIR"
    run install -d "$SESSION_DIR"
    run cd         "$SESSION_DIR"

    wfetch "$1" -o self --no-buffer

    run chmod 755 self

    if [ -w "$CURRENT_SCRIPT_REALPATH" ] ; then
        run      mv self "$CURRENT_SCRIPT_REALPATH"
    else
        run sudo mv self "$CURRENT_SCRIPT_REALPATH"
    fi

    run rm -rf "$SESSION_DIR"
}

# }}}
##############################################################################
# {{{ ndk-pkg integrate zsh

# __integrate_zsh_completions <URL> [--output-dir=<DIR>]
  __integrate_zsh_completions() {
    [ -z "$1" ] && abort 1 "__integrate_zsh_completions <URL> [--output-dir=<DIR>] , <URL> must be non-empty."

    ZSH_COMPLETIONS_SCRIPT_URL="$1"

    shift

    unset OUTPUT_DIR

    for arg in "$@"
    do
        case $arg in
            --output-dir=*)
                OUTPUT_DIR="${1#*=}"

                case $OUTPUT_DIR in
                    '')   abort 1 "__integrate_zsh_completions <URL> [--output-dir=<DIR>] , <DIR> must be a non-empty string." ;;
                    \~)   OUTPUT_DIR="$HOME" ;;
                    \~/)  OUTPUT_DIR="$HOME" ;;
                    \~/.) OUTPUT_DIR="$HOME" ;;
                    \~/*) OUTPUT_DIR="$HOME/$(printf '%s\n' "$1" | cut -c3-)" ;;
                esac

                ;;
            *)  abort 1 "__integrate_zsh_completions <URL> [--output-dir=<DIR>] , unrecognized argument: $arg"
        esac
    done

    ZSH_COMPLETIONS_SCRIPT_FILENAME="_$(basename "$NDKPKG_ARG0")"

    if [ -n "$OUTPUT_DIR" ] ; then
        ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH="$OUTPUT_DIR/$ZSH_COMPLETIONS_SCRIPT_FILENAME"
    elif [ "$(uname)" = Linux ] && command -v termux-info > /dev/null && [ "$HOME" = '/data/data/com.termux/files/home' ] ; then
        ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH="/data/data/com.termux/files/usr/share/zsh/site-functions/$ZSH_COMPLETIONS_SCRIPT_FILENAME"
    else
        ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH="/usr/local/share/zsh/site-functions/$ZSH_COMPLETIONS_SCRIPT_FILENAME"
    fi

    # if file exists and is a symbolic link
    if [ -L "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH" ] ; then
        # https://unix.stackexchange.com/questions/136494/whats-the-difference-between-realpath-and-readlink-f#:~:text=GNU%20coreutils%20introduced%20a%20realpath,in%20common%20with%20GNU%20readlink%20.
        if command -v realpath > /dev/null ; then
            ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH=$(realpath "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH")
        elif command -v readlink > /dev/null && readlink -f xx > /dev/null 2>&1 ; then
            ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH=$(readlink -f "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH")
        else
            ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH=$(realpath "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH")
        fi
    fi

    SESSION_DIR="$NDKPKG_HOME/run/$$"

    run rm -rf     "$SESSION_DIR"
    run install -d "$SESSION_DIR"
    run cd         "$SESSION_DIR"

    wfetch "$ZSH_COMPLETIONS_SCRIPT_URL" -o _ndk-pkg --no-buffer

    run chmod 644 _ndk-pkg

    if [ -f "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH" ] ; then
        if [ -w "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH" ] ; then
            run      mv _ndk-pkg "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH"
        else
            run sudo mv _ndk-pkg "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH"
        fi
    else
        ZSH_COMPLETIONS_SCRIPT_OUT_DIR="$(dirname "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH")"

        if [ ! -d "$ZSH_COMPLETIONS_SCRIPT_OUT_DIR" ] ; then
            run install -d "$ZSH_COMPLETIONS_SCRIPT_OUT_DIR" || run sudo install -d "$ZSH_COMPLETIONS_SCRIPT_OUT_DIR"
        fi

        if [ -w "$ZSH_COMPLETIONS_SCRIPT_OUT_DIR" ] ; then
            run      mv _ndk-pkg "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH"
        else
            run sudo mv _ndk-pkg "$ZSH_COMPLETIONS_SCRIPT_OUT_FILEPATH"
        fi
    fi

    run rm -rf "$SESSION_DIR"

    printf '\n'
    note "${COLOR_YELLOW}you may need to run command${COLOR_RED} ${COLOR_GREEN}autoload -U compinit && compinit${COLOR_OFF} ${COLOR_YELLOW}in zsh to make it work.${COLOR_OFF}"
}

# }}}
##############################################################################
# {{{ ndk-pkg gen-url-transform-sample

__gen_url_transform_sample() {
    SESSION_DIR="$NDKPKG_HOME/run/$$"

    rm -rf     "$SESSION_DIR"
    install -d "$SESSION_DIR"
    cd         "$SESSION_DIR"

    cat > url-transform.sample <<EOF
#!/bin/sh

case \$1 in
    *githubusercontent.com/*)
        printf 'https://ghp.ci/%s\n' "\$1"
        ;;
    https://github.com/*)
        printf 'https://ghp.ci/%s\n' "\$1"
        ;;
    '') printf '%s\n' "\$0 <URL>, <URL> is unspecified." >&2 ; exit 1 ;;
    *)  printf '%s\n' "\$1"
esac
EOF

    chmod +x url-transform.sample

    install -d "$NDKPKG_HOME"

    mv url-transform.sample "$NDKPKG_HOME/"

    rm -rf "$SESSION_DIR"

    success "url-transform sample has been written into $NDKPKG_HOME/url-transform.sample"
    note "You can rename url-transform.sample to url-transform then edit it to meet your needs. To apply this, you should run 'export NDKPKG_URL_TRANSFORM=$NDKPKG_HOME/url-transform' in your terminal."
}

# }}}
##############################################################################
# {{{ ndk-pkg depends

# __show_packages_depended_by_the_given_package <PACKAGE-NAME> [-t <d2|dot|box|svg|png>] [-o <OUTPUT-PATH>]
__show_packages_depended_by_the_given_package() {
    [ -z "$1" ] && abort 1 "$NDKPKG_ARG0 depends <PACKAGE-NAME> [-t <OUTPUT-TYPE>] [-o <OUTPUT-PATH>], <PACKAGE-NAME> is unspecified."

    PACKAGE_NAME="$1"

    shift

    ###########################################################################################

    unset OUTPUT_TYPE
    unset OUTPUT_PATH

    while [ -n "$1" ]
    do
        case $1 in
            -t) shift
                case $1 in
                    d2|dot|box|svg|png)
                        OUTPUT_TYPE="$1" ;;
                    '') abort 1 "$NDKPKG_ARG0 depends <PACKAGE-NAME> [-t <OUTPUT-TYPE>] [-o <OUTPUT-PATH>], -t option is given but <OUTPUT-TYPE> is unspecified." ;;
                    *)  abort 1 "$NDKPKG_ARG0 depends <PACKAGE-NAME> [-t <OUTPUT-TYPE>], unsupported <OUTPUT-TYPE>: $1, <OUTPUT-TYPE> should be one of dot|d2|box|svg|png"
                esac
                ;;
            -o) shift
                if [ -z "$1" ] ; then
                    abort 1 "$NDKPKG_ARG0 depends <PACKAGE-NAME> [-t <OUTPUT-TYPE>] [-o <OUTPUT-PATH>], -o option is given but <OUTPUT-PATH> is unspecified."
                else
                    OUTPUT_PATH="$1"
                fi
                ;;
            *)  abort 1 "$NDKPKG_ARG0 depends <PACKAGE-NAME> [-t <OUTPUT-TYPE>] [-o <OUTPUT-PATH>] [-K], unrecognized option: $1"
        esac
        shift
    done

    ###########################################################################################

    unset OUTPUT_DIR
    unset OUTPUT_FILEPATH

    case $OUTPUT_PATH in
        '')
            if [ -z "$OUTPUT_TYPE" ] ; then
                OUTPUT_TYPE='box'
            fi
            ;;
        ..|../)
            if [ -z "$OUTPUT_TYPE" ] ; then
                OUTPUT_TYPE='box'
            fi

            OUTPUT_FILEPATH="$PWD/../$PACKAGE_NAME-dependencies.$OUTPUT_TYPE"
            ;;
        .|./)
            if [ -z "$OUTPUT_TYPE" ] ; then
                OUTPUT_TYPE='box'
            fi

            OUTPUT_FILEPATH="$PWD/$PACKAGE_NAME-dependencies.$OUTPUT_TYPE"
            ;;
        */)
            if [ -z "$OUTPUT_TYPE" ] ; then
                OUTPUT_TYPE='box'
            fi

            case $OUTPUT_PATH in
                /*) OUTPUT_DIR="$OUTPUT_PATH" ;;
                *)  OUTPUT_DIR="$PWD/$OUTPUT_PATH"
            esac

            OUTPUT_FILEPATH="$OUTPUT_DIR/$PACKAGE_NAME-dependencies.$OUTPUT_TYPE"
            ;;
        *)
            if [ -z "$OUTPUT_TYPE" ] ; then
                case $OUTPUT_PATH in
                    *.box) OUTPUT_TYPE='box' ;;
                    *.dot) OUTPUT_TYPE='dot' ;;
                    *.d2)  OUTPUT_TYPE='d2'  ;;
                    *.svg) OUTPUT_TYPE='svg' ;;
                    *.png) OUTPUT_TYPE='png' ;;
                    *)     OUTPUT_TYPE='box' ;;
                esac
            fi

            case $OUTPUT_PATH in
                /*) OUTPUT_DIR="${OUTPUT_PATH%/*}"
                    OUTPUT_FILEPATH="$OUTPUT_PATH"
                    ;;
                *)  OUTPUT_DIR="$PWD/$(dirname "$OUTPUT_PATH")"
                    OUTPUT_FILEPATH="$PWD/$OUTPUT_PATH"
            esac
    esac

    ###########################################################################################

    unset ENGIN

    if [ "$OUTPUT_TYPE" = svg ] || [ "$OUTPUT_TYPE" = png ] ; then
        if command -v dot > /dev/null ; then
            ENGIN=dot
        elif command -v dot_static > /dev/null ; then
            ENGIN=dot_static
        elif command -v d2 > /dev/null ; then
            ENGIN=d2
        else
            abort 1 'none of dot, dot_static, d2 commands was found. please install graphviz or d2 package, then try again.'
        fi
    fi

    ###########################################################################################

    unset LINES

    unset DIRECTED_PATH_LIST

    STACK="$PACKAGE_NAME"

    while [ -n "$STACK" ]
    do
        case $STACK in
            *\;*) PACKAGE_NAME="${STACK##*;}" ; STACK="${STACK%;*}" ;;
            *)    PACKAGE_NAME="${STACK}"     ; STACK=
        esac

        ################################################################

        __load_formula_of_the_given_package "$PACKAGE_NAME"

        ################################################################

        [ -z "$PACKAGE_DEP_PKG" ] && continue

        ################################################################

        if [ "$OUTPUT_TYPE" = d2 ] || [ "$ENGIN" = d2 ] ; then
            for item in $PACKAGE_DEP_PKG
            do
                if [ -z "$LINES" ] ; then
                    LINES="$PACKAGE_NAME -> $item"
                else
                    LINES="$LINES
$PACKAGE_NAME -> $item"
                fi
            done
        else
            unset X; X="$(printf '"%s" ' $PACKAGE_DEP_PKG)"
            unset Y; Y="$(printf '    "%s" -> { %s}\n' "$PACKAGE_NAME" "$X")"

            if [ -z "$LINES" ] ; then
                LINES="$Y"
            else
                LINES="$(printf '%s\n%s\n' "$LINES" "$Y")"
            fi
        fi

        ################################################################

        DIRECTED_PATH_LIST_PART1=
        DIRECTED_PATH_LIST_PART2=

        for DIRECTED_PATH in $DIRECTED_PATH_LIST
        do
            case $DIRECTED_PATH in
                *\>"$PACKAGE_NAME")
                    DIRECTED_PATH_LIST_PART1="$DIRECTED_PATH_LIST_PART1 $DIRECTED_PATH" ;;
                *)  DIRECTED_PATH_LIST_PART2="$DIRECTED_PATH_LIST_PART2 $DIRECTED_PATH" ;;
            esac
        done

        ################################################################

        # it will be recalculated
        DIRECTED_PATH_LIST=

        ################################################################

        for DEPENDENT_PACKAGE_NAME in $PACKAGE_DEP_PKG
        do
            if [ "$DEPENDENT_PACKAGE_NAME" = "$PACKAGE_NAME" ] ; then
                abort 1 "package [$PACKAGE_NAME] depends itself."
            fi

            ############################################################

            if [ -z "$DIRECTED_PATH_LIST_PART1" ] ; then
                DIRECTED_PATH_LIST="$DIRECTED_PATH_LIST $PACKAGE_NAME>$DEPENDENT_PACKAGE_NAME"
            else
                for DIRECTED_PATH in $DIRECTED_PATH_LIST_PART1
                do
                    export IFS='>'

                    # check if have duplicate nodes in every directed path
                    for node in $DIRECTED_PATH
                    do
                        if [ "$node" = "$DEPENDENT_PACKAGE_NAME" ] ; then
                            abort 1 "depends has circle: $DIRECTED_PATH>$DEPENDENT_PACKAGE_NAME"
                        fi
                    done

                    unset IFS

                    DIRECTED_PATH_LIST="$DIRECTED_PATH_LIST $DIRECTED_PATH>$DEPENDENT_PACKAGE_NAME"
                done
            fi

            ############################################################

            if [ -z "$STACK" ] ; then
                STACK="$DEPENDENT_PACKAGE_NAME"
            else
                STACK="$STACK;$DEPENDENT_PACKAGE_NAME"
            fi
        done

        DIRECTED_PATH_LIST="$DIRECTED_PATH_LIST $DIRECTED_PATH_LIST_PART2"
    done

    ###########################################################################################

    [ -z "$LINES" ] && return 0

    ###########################################################################################

    case $OUTPUT_TYPE in
        dot)
            if [ -z "$OUTPUT_FILEPATH" ] ; then
                printf 'digraph G {\n%s\n}\n' "$LINES"
            else
                SESSION_DIR="$NDKPKG_HOME/run/$$"

                rm -rf     "$SESSION_DIR"
                install -d "$SESSION_DIR"
                cd         "$SESSION_DIR"

                printf 'digraph G {\n%s\n}\n' "$LINES" > dependencies.dot

                if [ -n "$OUTPUT_DIR" ] && [ ! -d "$OUTPUT_DIR" ] ; then
                    install -d "$OUTPUT_DIR"
                fi

                mv -T dependencies.dot "$OUTPUT_FILEPATH"

                rm -rf "$SESSION_DIR"
            fi
            ;;
        d2)
            if [ -z "$OUTPUT_FILEPATH" ] ; then
                printf '%s\n' "$LINES"
            else
                SESSION_DIR="$NDKPKG_HOME/run/$$"

                rm -rf     "$SESSION_DIR"
                install -d "$SESSION_DIR"
                cd         "$SESSION_DIR"

                printf '%s\n' "$LINES" > dependencies.d2

                if [ -n "$OUTPUT_DIR" ] && [ ! -d "$OUTPUT_DIR" ] ; then
                    install -d "$OUTPUT_DIR"
                fi

                mv -T dependencies.d2 "$OUTPUT_FILEPATH"

                rm -rf "$SESSION_DIR"
            fi
            ;;
        box)
            DOT_CONTENT="digraph G {
$LINES
}"

            if [ -z "$OUTPUT_FILEPATH" ] ; then
                # https://github.com/ggerganov/dot-to-ascii
                curl \
                    -s \
                    -G \
                    --data-urlencode "boxart=1" \
                    --data-urlencode "src=$DOT_CONTENT" \
                    'https://dot-to-ascii.ggerganov.com/dot-to-ascii.php'
            else
                SESSION_DIR="$NDKPKG_HOME/run/$$"

                rm -rf     "$SESSION_DIR"
                install -d "$SESSION_DIR"
                cd         "$SESSION_DIR"

                # https://github.com/ggerganov/dot-to-ascii
                curl \
                    -s \
                    -G \
                    -o dependencies.box \
                    --data-urlencode "boxart=1" \
                    --data-urlencode "src=$DOT_CONTENT" \
                    'https://dot-to-ascii.ggerganov.com/dot-to-ascii.php'

                if [ -n "$OUTPUT_DIR" ] && [ ! -d "$OUTPUT_DIR" ] ; then
                    install -d "$OUTPUT_DIR"
                fi

                mv dependencies.box "$OUTPUT_FILEPATH"

                rm -rf "$SESSION_DIR"
            fi
            ;;
        svg|png)
            if [ "$ENGIN" = d2 ] ; then
                if [ -z "$OUTPUT_FILEPATH" ] ; then
                    printf '%s\n' "$LINES" | d2 -
                else
                    if [ -n "$OUTPUT_DIR" ] && [ ! -d "$OUTPUT_DIR" ] ; then
                        install -d "$OUTPUT_DIR"
                    fi

                    printf '%s\n' "$LINES" | d2 - "$OUTPUT_FILEPATH"
                fi
            else
                SESSION_DIR="$NDKPKG_HOME/run/$$"

                rm -rf     "$SESSION_DIR"
                install -d "$SESSION_DIR"
                cd         "$SESSION_DIR"

                printf 'digraph G {\n%s\n}\n' "$LINES" > dependencies.dot

                if [ -z "$OUTPUT_FILEPATH" ] ; then
                    "$ENGIN" "-T$OUTPUT_TYPE" dependencies.dot
                else
                    "$ENGIN" "-T$OUTPUT_TYPE" -o dependencies.tmp dependencies.dot

                    if [ -n "$OUTPUT_DIR" ] && [ ! -d "$OUTPUT_DIR" ] ; then
                        install -d "$OUTPUT_DIR"
                    fi

                    mv -T dependencies.tmp "$OUTPUT_FILEPATH"
                fi

                rm -rf "$SESSION_DIR"
            fi
    esac
}

# }}}
##############################################################################
# {{{ ndk-pkg uninstall

__uninstall_the_given_packages() {
    [ -z "$1" ] && abort 1 "neither package-name nor package-spec is specified."

    unset PACKAGE_SPECS

    for item in "$@"
    do
        PACKAGE_SPECS="$PACKAGE_SPECS $(inspect_package_spec "$item")"
    done

    for PACKAGE_SPEC in $PACKAGE_SPECS
    do
        PACKAGE_INSTALLED_LINK_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC"

        [ -e "$PACKAGE_INSTALLED_LINK_DIR" ] || abort 10 "package '$PACKAGE_SPEC' is not installed."
        [ -L "$PACKAGE_INSTALLED_LINK_DIR" ] || abort 11 "$PACKAGE_INSTALLED_LINK_DIR was expected a symlink, but it was not."
        [ -d "$PACKAGE_INSTALLED_LINK_DIR" ] || abort 12 "$PACKAGE_INSTALLED_LINK_DIR was expected a symlink refer to a directory, but it was not."

        PACKAGE_INSTALLED_REAL_DIR="$(readlink -f "$PACKAGE_INSTALLED_LINK_DIR")"

        [ -d "$PACKAGE_INSTALLED_REAL_DIR" ] || abort 13 "directory $PACKAGE_INSTALLED_REAL_DIR was expected exists, but it was not."

        PACKAGE_MANIFEST_FILEPATH="$PACKAGE_INSTALLED_REAL_DIR/.ndk-pkg/MANIFEST.txt"

        [ -f "$PACKAGE_MANIFEST_FILEPATH" ] || abort 13 "$PACKAGE_MANIFEST_FILEPATH file was expected exist, but it was not."

        PACKAGE_RECEIPT_FILEPATH="$PACKAGE_INSTALLED_REAL_DIR/.ndk-pkg/RECEIPT.yml"

        [ -f "$PACKAGE_RECEIPT_FILEPATH" ] || abort 14 "$PACKAGE_RECEIPT_FILEPATH file was expected exist, but it was not."

        run rm -ff "$PACKAGE_INSTALLED_LINK_DIR"
        run rm -rf "$PACKAGE_INSTALLED_REAL_DIR"
    done
}

# }}}
##############################################################################
# {{{ ndk-pkg reinstall

__reinstall_the_given_packages() {
    inspect_install_arguments "$@"

    [ -z "$SPECIFIED_PACKAGE_SPEC_LIST" ] && abort 1 "neither package-name nor package-spec is specified."

    #########################################################################################

    SESSION_DIR="$NDKPKG_HOME/run/$$"

    rm -rf     "$SESSION_DIR"
    install -d "$SESSION_DIR"

    #########################################################################################

    # 1. check if has circle
    # 2. backup formulas
    # 3. cache variables

    for SPECIFIED_PACKAGE_SPEC in $SPECIFIED_PACKAGE_SPEC_LIST
    do
        PACKAGE_NAME_STACK="${SPECIFIED_PACKAGE_SPEC##*/}"

        while [ -n "$PACKAGE_NAME_STACK" ]
        do
            case $PACKAGE_NAME_STACK in
                *\;*) PACKAGE_NAME="${PACKAGE_NAME_STACK##*;}" ; PACKAGE_NAME_STACK="${PACKAGE_NAME_STACK%;*}" ;;
                *)    PACKAGE_NAME="${PACKAGE_NAME_STACK}"     ; PACKAGE_NAME_STACK=
            esac

            if [ -f "$SESSION_DIR/$PACKAGE_NAME.yml" ] ; then
                continue
            fi

            __load_formula_of_the_given_package "$PACKAGE_NAME"

            cp -L "$PACKAGE_FORMULA_FILEPATH" "$SESSION_DIR/$PACKAGE_NAME.yml"

            eval "PACKAGE_DEP_PKG_${PACKAGE_NAME_UPPERCASE_UNDERSCORE}='$PACKAGE_DEP_PKG'"

            for DEPENDENT_PACKAGE_NAME in $PACKAGE_DEP_PKG
            do
                if [ "$DEPENDENT_PACKAGE_NAME" = "$PACKAGE_NAME" ] ; then
                    abort 1 "package '$PACKAGE_NAME' depends itself."
                fi

                if [ -z "$PACKAGE_NAME_STACK" ] ; then
                    PACKAGE_NAME_STACK="$DEPENDENT_PACKAGE_NAME"
                else
                    PACKAGE_NAME_STACK="$PACKAGE_NAME_STACK;$DEPENDENT_PACKAGE_NAME"
                fi
            done
        done
    done

    #########################################################################################

    for SPECIFIED_PACKAGE_SPEC in $SPECIFIED_PACKAGE_SPEC_LIST
    do
        TARGET_PLATFORM_SPEC="${SPECIFIED_PACKAGE_SPEC%/*}"

        TARGET_PLATFORM_NAME=
        TARGET_PLATFORM_VERS=
        TARGET_PLATFORM_ARCH=

        TARGET_PLATFORM_NAME="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -d- -f1)"
        TARGET_PLATFORM_VERS="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -d- -f2)"
        TARGET_PLATFORM_ARCH="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -c 12-)"

        ##################################################################

        REQUESTED_PACKAGE_NAME_LIST=

        PACKAGE_NAME_STACK="${SPECIFIED_PACKAGE_SPEC##*/}"

        while [ -n "$PACKAGE_NAME_STACK" ]
        do
            case $PACKAGE_NAME_STACK in
                *\;*) PACKAGE_NAME="${PACKAGE_NAME_STACK##*;}" ; PACKAGE_NAME_STACK="${PACKAGE_NAME_STACK%;*}" ;;
                *)    PACKAGE_NAME="${PACKAGE_NAME_STACK}"     ; PACKAGE_NAME_STACK=
            esac

            ##################################################################

            REQUESTED_PACKAGE_NAME_LIST2="$PACKAGE_NAME"

            for item in $REQUESTED_PACKAGE_NAME_LIST
            do
                [ "$item" = "$PACKAGE_NAME" ] && continue
                REQUESTED_PACKAGE_NAME_LIST2="$REQUESTED_PACKAGE_NAME_LIST2 $item"
            done

            REQUESTED_PACKAGE_NAME_LIST="$REQUESTED_PACKAGE_NAME_LIST2"

            ##################################################################

            PACKAGE_NAME_UPPERCASE_UNDERSCORE="$(printf '%s\n' "$PACKAGE_NAME" | tr a-z A-Z | tr '@+-.' '_')"

            for item in $(eval echo \$PACKAGE_DEP_PKG_"${PACKAGE_NAME_UPPERCASE_UNDERSCORE}")
            do
                if [ -z "$PACKAGE_NAME_STACK" ] ; then
                    PACKAGE_NAME_STACK="$item"
                else
                    PACKAGE_NAME_STACK="$PACKAGE_NAME_STACK;$item"
                fi
            done
        done

        ##################################################################

        for PACKAGE_NAME in $REQUESTED_PACKAGE_NAME_LIST
        do
            PACKAGE_SPEC="$TARGET_PLATFORM_SPEC/$PACKAGE_NAME"

            PACKAGE_INSTALLED_LINK_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC"
            PACKAGE_INSTALLED_REAL_DIR="$(readlink -f "$PACKAGE_INSTALLED_LINK_DIR")"

            (__install_the_given_package "$PACKAGE_SPEC")

            rm -rf "$PACKAGE_INSTALLED_REAL_DIR"
        done
    done

    #########################################################################################

    if [ "$REQUEST_TO_KEEP_SESSION_DIR" != 1 ] ; then
        rm -rf "$SESSION_DIR"
    fi
}

# }}}
##############################################################################
# {{{ ndk-pkg upgrade

__upgrade_packages() {
    inspect_install_arguments "$@"

    if [ -z "$SPECIFIED_PACKAGE_SPEC_LIST" ] ; then
        SPECIFIED_PACKAGE_SPEC_LIST=$(__list__outdated_packages)
    fi

    if [ -z "$SPECIFIED_PACKAGE_SPEC_LIST" ] ; then
        return 0
    fi

    #########################################################################################

    SESSION_DIR="$NDKPKG_HOME/run/$$"

    rm -rf     "$SESSION_DIR"
    install -d "$SESSION_DIR"

    #########################################################################################

    # 1. check if has circle
    # 2. backup formulas
    # 3. cache variables

    for SPECIFIED_PACKAGE_SPEC in $SPECIFIED_PACKAGE_SPEC_LIST
    do
        PACKAGE_NAME_STACK="${SPECIFIED_PACKAGE_SPEC##*/}"

        while [ -n "$PACKAGE_NAME_STACK" ]
        do
            case $PACKAGE_NAME_STACK in
                *\;*) PACKAGE_NAME="${PACKAGE_NAME_STACK##*;}" ; PACKAGE_NAME_STACK="${PACKAGE_NAME_STACK%;*}" ;;
                *)    PACKAGE_NAME="${PACKAGE_NAME_STACK}"     ; PACKAGE_NAME_STACK=
            esac

            if [ -f "$SESSION_DIR/$PACKAGE_NAME.yml" ] ; then
                continue
            fi

            __load_formula_of_the_given_package "$PACKAGE_NAME"

            cp -L "$PACKAGE_FORMULA_FILEPATH" "$SESSION_DIR/$PACKAGE_NAME.yml"

            eval "PACKAGE_DEP_PKG_${PACKAGE_NAME_UPPERCASE_UNDERSCORE}='$PACKAGE_DEP_PKG'"

            for DEPENDENT_PACKAGE_NAME in $PACKAGE_DEP_PKG
            do
                if [ "$DEPENDENT_PACKAGE_NAME" = "$PACKAGE_NAME" ] ; then
                    abort 1 "package '$PACKAGE_NAME' depends itself."
                fi

                if [ -z "$PACKAGE_NAME_STACK" ] ; then
                    PACKAGE_NAME_STACK="$DEPENDENT_PACKAGE_NAME"
                else
                    PACKAGE_NAME_STACK="$PACKAGE_NAME_STACK;$DEPENDENT_PACKAGE_NAME"
                fi
            done
        done
    done

    #########################################################################################

    for SPECIFIED_PACKAGE_SPEC in $SPECIFIED_PACKAGE_SPEC_LIST
    do
        TARGET_PLATFORM_SPEC="${SPECIFIED_PACKAGE_SPEC%/*}"

        TARGET_PLATFORM_NAME=
        TARGET_PLATFORM_VERS=
        TARGET_PLATFORM_ARCH=

        TARGET_PLATFORM_NAME="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -d- -f1)"
        TARGET_PLATFORM_VERS="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -d- -f2)"
        TARGET_PLATFORM_ARCH="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -c 12-)"

        ##################################################################

        REQUESTED_PACKAGE_NAME_LIST=

        PACKAGE_NAME_STACK="${SPECIFIED_PACKAGE_SPEC##*/}"

        while [ -n "$PACKAGE_NAME_STACK" ]
        do
            case $PACKAGE_NAME_STACK in
                *\;*) PACKAGE_NAME="${PACKAGE_NAME_STACK##*;}" ; PACKAGE_NAME_STACK="${PACKAGE_NAME_STACK%;*}" ;;
                *)    PACKAGE_NAME="${PACKAGE_NAME_STACK}"     ; PACKAGE_NAME_STACK=
            esac

            ##################################################################

            REQUESTED_PACKAGE_NAME_LIST2="$PACKAGE_NAME"

            for item in $REQUESTED_PACKAGE_NAME_LIST
            do
                [ "$item" = "$PACKAGE_NAME" ] && continue
                REQUESTED_PACKAGE_NAME_LIST2="$REQUESTED_PACKAGE_NAME_LIST2 $item"
            done

            REQUESTED_PACKAGE_NAME_LIST="$REQUESTED_PACKAGE_NAME_LIST2"

            ##################################################################

            PACKAGE_NAME_UPPERCASE_UNDERSCORE="$(printf '%s\n' "$PACKAGE_NAME" | tr a-z A-Z | tr '@+-.' '_')"

            for item in $(eval echo \$PACKAGE_DEP_PKG_"${PACKAGE_NAME_UPPERCASE_UNDERSCORE}")
            do
                if [ -z "$PACKAGE_NAME_STACK" ] ; then
                    PACKAGE_NAME_STACK="$item"
                else
                    PACKAGE_NAME_STACK="$PACKAGE_NAME_STACK;$item"
                fi
            done
        done

        ##################################################################

        for PACKAGE_NAME in $REQUESTED_PACKAGE_NAME_LIST
        do
            PACKAGE_SPEC="$TARGET_PLATFORM_SPEC/$PACKAGE_NAME"

            is_package__outdated "$PACKAGE_SPEC" || {
                note 1 "$PACKAGE_SPEC is not outdated."
                continue
            }

            PACKAGE_INSTALLED_LINK_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC"
            PACKAGE_INSTALLED_REAL_DIR="$(readlink -f "$PACKAGE_INSTALLED_LINK_DIR")"

            (__install_the_given_package "$PACKAGE_SPEC")

            rm -rf "$PACKAGE_INSTALLED_REAL_DIR"
        done
    done

    #########################################################################################

    if [ "$REQUEST_TO_KEEP_SESSION_DIR" != 1 ] ; then
        rm -rf "$SESSION_DIR"
    fi
}

# }}}
##############################################################################
# {{{ ndk-pkg install

# inspect_package_spec <PACKAGE-NAME|PACKAGE-SPEC>
  inspect_package_spec() {
    case $1 in
        '') abort 1 "neither package-name nor package-spec is specified."
            ;;
        */*)
            case $1 in
                android-[1-9][0-9]-armeabi-v7a/*)
                    TARGET_PLATFORM_ABI='armeabi-v7a'
                    ;;
                android-[1-9][0-9]-arm64-v8a/*)
                    TARGET_PLATFORM_ABI='arm64-v8a'
                    ;;
                android-[1-9][0-9]-x86/*)
                    TARGET_PLATFORM_ABI='x86'
                    ;;
                android-[1-9][0-9]-x86_64/*)
                    TARGET_PLATFORM_ABI='x86_64'
                    ;;
                *)  abort 1 "invalid package spec: $1"
            esac 

            PACKAGE_NAME="${1##*/}"

            if [ -z "$PACKAGE_NAME" ] ; then
                abort 1 "invalid package spec: $1\n    package name is unspecifed."
            elif printf '%s\n' "$PACKAGE_NAME" | grep -q -E '^[A-Za-z0-9+-_.@]{1,50}$' ; then
                :
            else
                abort 1 "invalid package spec: $1\n    package name must match the regular expression pattern: ^[A-Za-z0-9+-_.@]{1,50}$"
            fi

            TARGET_PLATFORM_SPEC="${1%/*}"
            TARGET_PLATFORM_VERS="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -d- -f2)"

            PACKAGE_SPEC="$1"
            ;;
        *)  if printf '%s\n' "$1" | grep -q -E '^[A-Za-z0-9+-_.@]{1,50}$' ; then
                case $NDKPKG_DEFAULT_TARGET in
                    '') NDKPKG_DEFAULT_TARGET='android-21-arm64-v8a'
                        ;;
                    android-[1-9][0-9]-armeabi-v7a)
                        ;;
                    android-[1-9][0-9]-arm64-v8a)
                        ;;
                    android-[1-9][0-9]-x86)
                        ;;
                    android-[1-9][0-9]-x86_64)
                        ;;
                    *) abort 1 "you have set NDKPKG_DEFAULT_TARGET=$NDKPKG_DEFAULT_TARGET , but it is an invalid target spec."
                esac

                PACKAGE_SPEC="$NDKPKG_DEFAULT_TARGET/$1"
            else
                abort 1 "invalid package name: $1"
            fi
    esac

    printf '%s\n' "$PACKAGE_SPEC"
}

# inspect_package_spec_list <PACKAGE-NAME | PACKAGE-SPECS> [USER-SPECIFIED-TARGET]
  inspect_package_spec_list() {
    case $1 in
        '') abort 1 "neither package-name nor package-specs is specified."
            ;;
        */*)
            PACKAGE_NAME="${1##*/}"

            if [ -z "$PACKAGE_NAME" ] ; then
                abort 1 "invalid package spec: $1\n    package name is unspecifed."
            elif printf '%s\n' "$PACKAGE_NAME" | grep -q -E '^[A-Za-z0-9+-_.@]{1,50}$' ; then
                :
            else
                abort 1 "invalid package spec: $1\n    package name must match the regular expression pattern: ^[A-Za-z0-9+-_.@]{1,50}$"
            fi

            TARGET_PLATFORM_SPEC="${1%/*}"

            case $TARGET_PLATFORM_SPEC in
                android-[1-9][0-9]-*) ;;
                *)  abort 1 "invalid package spec: $1"
            esac

            TARGET_PLATFORM_VERS="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -d- -f2)"
            TARGET_PLATFORM_ABIS="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -c 12- | tr ',' ' ')"

            unset PACKAGE_SPEC_LIST

            for TARGET_PLATFORM_ABI in $TARGET_PLATFORM_ABIS
            do
                case $TARGET_PLATFORM_ABI in
                    armeabi-v7a|arm64-v8a|x86|x86_64)
                        PACKAGE_SPEC_LIST="$PACKAGE_SPEC_LIST android-$TARGET_PLATFORM_VERS-$TARGET_PLATFORM_ABI/$PACKAGE_NAME"
                        ;;
                    *)  abort 1 "invalid package spec: $1\n    unrecognized android abi: $TARGET_PLATFORM_ABI"
                esac
            done

            printf '%s\n' "$PACKAGE_SPEC_LIST"
            ;;
        *)  if printf '%s\n' "$1" | grep -q -E '^[A-Za-z0-9+-_.@]{1,50}$' ; then
                if [ -z "$2" ] ; then
                    case $NDKPKG_DEFAULT_TARGET in
                        '') NDKPKG_DEFAULT_TARGET='android-21-arm64-v8a'
                            ;;
                        android-[1-9][0-9]-armeabi-v7a)
                            ;;
                        android-[1-9][0-9]-arm64-v8a)
                            ;;
                        android-[1-9][0-9]-x86)
                            ;;
                        android-[1-9][0-9]-x86_64)
                            ;;
                        *) abort 1 "you have set environment variable NDKPKG_DEFAULT_TARGET=$NDKPKG_DEFAULT_TARGET , but it is an invalid android spec."
                    esac

                    printf '%s\n' "$NDKPKG_DEFAULT_TARGET/$1"
                else
                    case $2 in
                        android-[1-9][0-9]-armeabi-v7a)
                            ;;
                        android-[1-9][0-9]-arm64-v8a)
                            ;;
                        android-[1-9][0-9]-x86)
                            ;;
                        android-[1-9][0-9]-x86_64)
                            ;;
                        *) abort 1 "you have specified --target=$2 argument, but it is an invalid android spec."
                    esac

                    printf '%s\n' "$2/$1"
                fi
            else
                abort 1 "invalid package name: $1"
            fi
    esac
}

inspect_install_arguments() {
    LOG_LEVEL_QUIET=0
    LOG_LEVEL_NORMAL=1
    LOG_LEVEL_VERBOSE=2
    LOG_LEVEL_VERY_VERBOSE=3

    unset PROFILE

    unset LOG_LEVEL

    unset BUILD_NJOBS

    unset ENABLE_LTO

    unset ENABLE_STRIP

    unset ENABLE_CCACHE

    unset REQUEST_TO_UPGRADE_IF_POSSIBLE

    unset REQUEST_TO_EXPORT_COMPILE_COMMANDS_JSON

    unset REQUEST_TO_KEEP_SESSION_DIR

    unset REQUEST_TO_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE

    unset SPECIFIED_FORMULA_SEARCH_DIRS

    unset SPECIFIED_PACKAGE_LIST

    unset SPECIFIED_TARGET

    unset ANDROID_NDK_HOME

    unset DUMP_ENV
    unset DUMP_HTTP
    unset DUMP_UPPM
    unset DUMP_NDK
    unset DUMP_SED
    unset DUMP_FORMULA

    unset VERBOSE_GO
    unset VERBOSE_CARGO
    unset VERBOSE_MESON
    unset VERBOSE_NINJA
    unset VERBOSE_GMAKE
    unset VERBOSE_CMAKE
    unset VERBOSE_XMAKE

    unset DEBUG_CC
    unset DEBUG_LD
    unset DEBUG_GO
    unset DEBUG_CARGO
    unset DEBUG_GMAKE
    unset DEBUG_CMAKE
    unset DEBUG_XMAKE
    unset DEBUG_PKG_CONFIG

    while [ -n "$1" ]
    do
        case $1 in
            -q) LOG_LEVEL=0 ;;
            -v) LOG_LEVEL=2

                DUMP_ENV=1
                DUMP_HTTP=1
                DUMP_UPPM=1
                DUMP_NDK=1
                DUMP_SED=1
                DUMP_FORMULA=1

                VERBOSE_GO=1
                VERBOSE_CARGO=1
                VERBOSE_MESON=1
                VERBOSE_NINJA=1
                VERBOSE_GMAKE=1
                VERBOSE_CMAKE=1
                VERBOSE_XMAKE=1
                ;;
            -x) LOG_LEVEL=3

                DUMP_ENV=1
                DUMP_HTTP=1
                DUMP_UPPM=1
                DUMP_NDK=1
                DUMP_SED=1
                DUMP_FORMULA=1

                VERBOSE_MESON=1
                VERBOSE_NINJA=1
                VERBOSE_GMAKE=1
                VERBOSE_CMAKE=1
                VERBOSE_XMAKE=1

                DEBUG_CC=1
                DEBUG_LD=1
                DEBUG_GO=1
                DEBUG_CARGO=1
                DEBUG_GMAKE=1
                DEBUG_CMAKE=1
                DEBUG_XMAKE=1
                DEBUG_PKG_CONFIG=1
                ;;
            -v-env)
                DUMP_ENV=1
                ;;
            -v-http)
                DUMP_HTTP=1
                ;;
            -v-uppm)
                DUMP_UPPM=1
                ;;
            -v-ndk)
                DUMP_NDK=1
                ;;
            -v-formula)
                DUMP_FORMULA=1
                ;;
            -v-go)
                VERBOSE_GO=1
                ;;
            -v-cargo)
                VERBOSE_CARGO=1
                ;;
            -v-meson)
                VERBOSE_MESON=1
                ;;
            -v-ninja)
                VERBOSE_NINJA=1
                ;;
            -v-gmake)
                VERBOSE_GMAKE=1
                ;;
            -v-cmake)
                VERBOSE_CMAKE=1
                ;;
            -v-xmake)
                VERBOSE_XMAKE=1
                ;;
            -x-sh)
                set -x
                ;;
            -x-cc)
                DEBUG_CC=1
                ;;
            -x-ld)
                DEBUG_LD=1
                ;;
            -x-go)
                DEBUG_GO=1
                ;;
            -x-cargo)
                DEBUG_CARGO=1
                ;;
            -x-gmake)
                DEBUG_GMAKE=1
                ;;
            -x-cmake)
                DEBUG_CMAKE=1
                ;;
            -x-xmake)
                DEBUG_XMAKE=1
                ;;
            -x-pkg-config)
                DEBUG_PKG_CONFIG=1
                ;;
            --disable-ccache)
                ENABLE_CCACHE=0
                ;;
            --enable-lto)
                ENABLE_LTO=1
                ;;
            --enable-strip)
                ENABLE_STRIP=all
                ;;
            --enable-strip=*)
                ENABLE_STRIP="${1#*=}"
                case $ENABLE_STRIP in
                    no|all|debug|unneeded) ;;
                    *)  abort 1 "--strip=<VALUE>, VALUE must be one of no, all, debug, unneeded"
                esac
                ;;
            --target=*)
                SPECIFIED_TARGET="${1#*=}"
                ;;
            --profile=*)
                PROFILE="${1#*=}"
                case $PROFILE in
                    '') abort 1 "--profile=<VALUE>, VALUE should not be empty." ;;
                    debug|release) ;;
                    *)  abort 1 "--profile=<VALUE>, VALUE must be either debug or release" ;;
                esac
                ;;
            --ndk-home=*)
                ANDROID_NDK_HOME="${1#*=}"
                ;;
            --static)
                REQUEST_TO_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE=1
                ;;
            -j) shift
                isInteger "$1" || abort 1 "-j <N>, <N> must be an integer."
                BUILD_NJOBS="$1"
                ;;
            -I) shift
                [ -z "$1" ] && abort 1 "-I <FORMULA-SEARCH-DIR> , <FORMULA-SEARCH-DIR> is unspecified."
                [ -e "$1" ] || abort 1 "'$1' was expected to be exist, but it was not."
                [ -d "$1" ] || abort 1 "'$1' was expected to be a directory, but it was not."
                FORMULA_SEARCH_DIR="$(realpath "$1")"
                SPECIFIED_FORMULA_SEARCH_DIRS="$SPECIFIED_FORMULA_SEARCH_DIRS
$FORMULA_SEARCH_DIR"
                ;;
            -K) REQUEST_TO_KEEP_SESSION_DIR=1 ;;
            -U) REQUEST_TO_UPGRADE_IF_POSSIBLE=1 ;;
            -E) REQUEST_TO_EXPORT_COMPILE_COMMANDS_JSON=1 ;;

            -y) ;;

            -*) abort 1 "unrecognized option: $1" ;;

            *)  SPECIFIED_PACKAGE_LIST="$SPECIFIED_PACKAGE_LIST $1"
        esac
        shift
    done

    #########################################################################################

    NDKPKG_FORMULA_SEARCH_DIRS="$SPECIFIED_FORMULA_SEARCH_DIRS"

    #########################################################################################

    unset SPECIFIED_PACKAGE_SPEC_LIST

    for PACKAGE in $SPECIFIED_PACKAGE_LIST
    do
        SPECIFIED_PACKAGE_SPEC_LIST="$SPECIFIED_PACKAGE_SPEC_LIST $(inspect_package_spec_list "$PACKAGE" "$SPECIFIED_TARGET")"
    done

    SPECIFIED_PACKAGE_SPEC_LIST=${SPECIFIED_PACKAGE_SPEC_LIST#' '}

    #########################################################################################

    if [ "$LOG_LEVEL" = 0 ] ; then
        exec 1>/dev/null
        exec 2>&1
    else
        if [ -z "$LOG_LEVEL" ] ; then
            LOG_LEVEL=1
        fi
    fi

    #########################################################################################

    if [ -z "$PROFILE" ] ; then
        PROFILE=release
    fi

    #########################################################################################

    if [ -z "$ENABLE_STRIP" ] ; then
        case $PROFILE in
            debug)   ENABLE_STRIP=no  ;;
            release) ENABLE_STRIP=all ;;
        esac
    fi

    #########################################################################################

    # these environment variables are not used by this script
    # https://cmake.org/cmake/help/latest/envvar/MACOSX_DEPLOYMENT_TARGET.html
    unset   MACOSX_DEPLOYMENT_TARGET
    unset IPHONEOS_DEPLOYMENT_TARGET
    unset  WATCHOS_DEPLOYMENT_TARGET

    # https://www.real-world-systems.com/docs/xcrun.1.html
    unset SDKROOT

    # https://stackoverflow.com/questions/18476490/what-is-purpose-of-target-arch-variable-in-makefiles
    unset TARGET_ARCH

    unset LIBS

    # these environment variables are override by this script
    unset        CC_FOR_BUILD
    unset      OBJC_FOR_BUILD
    unset       CXX_FOR_BUILD
    unset       CPP_FOR_BUILD
    unset        AS_FOR_BUILD
    unset        AR_FOR_BUILD
    unset    RANLIB_FOR_BUILD
    unset        LD_FOR_BUILD
    unset        NM_FOR_BUILD
    unset      SIZE_FOR_BUILD
    unset     STRIP_FOR_BUILD
    unset   STRINGS_FOR_BUILD
    unset   OBJDUMP_FOR_BUILD
    unset   OBJCOPY_FOR_BUILD
    unset   READELF_FOR_BUILD
    unset   SYSROOT_FOR_BUILD

    unset    CFLAGS_FOR_BUILD
    unset  CXXFLAGS_FOR_BUILD
    unset OBJCFLAGS_FOR_BUILD
    unset   LDFLAGS_FOR_BUILD

    #########################################################################################

    if [ "$DEBUG_CC" = 1 ] ; then
        # this environment variable is used by wrapper-target-*
        export NDKPKG_VERBOSE=1
    else
        unset  NDKPKG_VERBOSE
    fi

    #########################################################################################

    setup_android_ndk_env "$ANDROID_NDK_HOME"
}

__install_the_given_packages() {
    inspect_install_arguments "$@"

    [ -z "$SPECIFIED_PACKAGE_SPEC_LIST" ] && abort 1 "$NDKPKG_ARG0 install <PACKAGE-SPEC|PACKAGE-NAME>..., <|PACKAGE-SPEC|PACKAGE-NAME> is unspecified."

    #########################################################################################

    SESSION_DIR="$NDKPKG_HOME/run/$$"

    rm -rf     "$SESSION_DIR"
    install -d "$SESSION_DIR"

    #########################################################################################

    # 1. check if has circle
    # 2. backup formulas
    # 3. cache variables

    for SPECIFIED_PACKAGE_SPEC in $SPECIFIED_PACKAGE_SPEC_LIST
    do
        PACKAGE_NAME_STACK="${SPECIFIED_PACKAGE_SPEC##*/}"

        while [ -n "$PACKAGE_NAME_STACK" ]
        do
            case $PACKAGE_NAME_STACK in
                *\;*) PACKAGE_NAME="${PACKAGE_NAME_STACK##*;}" ; PACKAGE_NAME_STACK="${PACKAGE_NAME_STACK%;*}" ;;
                *)    PACKAGE_NAME="${PACKAGE_NAME_STACK}"     ; PACKAGE_NAME_STACK=
            esac

            if [ -f "$SESSION_DIR/$PACKAGE_NAME.yml" ] ; then
                continue
            fi

            __load_formula_of_the_given_package "$PACKAGE_NAME"

            cp -L "$PACKAGE_FORMULA_FILEPATH" "$SESSION_DIR/$PACKAGE_NAME.yml"

            eval "PACKAGE_DEP_PKG_${PACKAGE_NAME_UPPERCASE_UNDERSCORE}='$PACKAGE_DEP_PKG'"

            for DEPENDENT_PACKAGE_NAME in $PACKAGE_DEP_PKG
            do
                if [ "$DEPENDENT_PACKAGE_NAME" = "$PACKAGE_NAME" ] ; then
                    abort 1 "package '$PACKAGE_NAME' depends itself."
                fi

                if [ -z "$PACKAGE_NAME_STACK" ] ; then
                    PACKAGE_NAME_STACK="$DEPENDENT_PACKAGE_NAME"
                else
                    PACKAGE_NAME_STACK="$PACKAGE_NAME_STACK;$DEPENDENT_PACKAGE_NAME"
                fi
            done
        done
    done

    #########################################################################################

    for SPECIFIED_PACKAGE_SPEC in $SPECIFIED_PACKAGE_SPEC_LIST
    do
        TARGET_PLATFORM_SPEC="${SPECIFIED_PACKAGE_SPEC%/*}"

        TARGET_PLATFORM_NAME=
        TARGET_PLATFORM_VERS=
        TARGET_PLATFORM_ARCH=

        TARGET_PLATFORM_NAME="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -d- -f1)"
        TARGET_PLATFORM_VERS="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -d- -f2)"
        TARGET_PLATFORM_ARCH="$(printf '%s\n' "$TARGET_PLATFORM_SPEC" | cut -c 12-)"

        ##################################################################

        REQUESTED_PACKAGE_NAME_LIST=

        PACKAGE_NAME_STACK="${SPECIFIED_PACKAGE_SPEC##*/}"

        while [ -n "$PACKAGE_NAME_STACK" ]
        do
            case $PACKAGE_NAME_STACK in
                *\;*) PACKAGE_NAME="${PACKAGE_NAME_STACK##*;}" ; PACKAGE_NAME_STACK="${PACKAGE_NAME_STACK%;*}" ;;
                *)    PACKAGE_NAME="${PACKAGE_NAME_STACK}"     ; PACKAGE_NAME_STACK=
            esac

            ##################################################################

            REQUESTED_PACKAGE_NAME_LIST2="$PACKAGE_NAME"

            for item in $REQUESTED_PACKAGE_NAME_LIST
            do
                [ "$item" = "$PACKAGE_NAME" ] && continue
                REQUESTED_PACKAGE_NAME_LIST2="$REQUESTED_PACKAGE_NAME_LIST2 $item"
            done

            REQUESTED_PACKAGE_NAME_LIST="$REQUESTED_PACKAGE_NAME_LIST2"

            ##################################################################

            PACKAGE_NAME_UPPERCASE_UNDERSCORE="$(printf '%s\n' "$PACKAGE_NAME" | tr a-z A-Z | tr '@+-.' '_')"

            for item in $(eval echo \$PACKAGE_DEP_PKG_"${PACKAGE_NAME_UPPERCASE_UNDERSCORE}")
            do
                if [ -z "$PACKAGE_NAME_STACK" ] ; then
                    PACKAGE_NAME_STACK="$item"
                else
                    PACKAGE_NAME_STACK="$PACKAGE_NAME_STACK;$item"
                fi
            done
        done

        ##################################################################

        for PACKAGE_NAME in $REQUESTED_PACKAGE_NAME_LIST
        do
            PACKAGE_SPEC="$TARGET_PLATFORM_SPEC/$PACKAGE_NAME"

            if is_package_installed "$PACKAGE_SPEC" ; then
                if [ "$UPGRAGE" = 1 ] ; then
                    if is_package__outdated "$PACKAGE_SPEC" ; then
                        (__install_the_given_package "$PACKAGE_SPEC")
                    else
                        if [ "$LOG_LEVEL" -ne 0 ] ; then
                            printf "$COLOR_GREEN%-10s$COLOR_OFF already have been installed and is up-to-date.\n" "$PACKAGE_SPEC"
                        fi
                    fi
                else
                    if [ "$LOG_LEVEL" -ne 0 ] ; then
                        printf "$COLOR_GREEN%-10s$COLOR_OFF already have been installed.\n" "$PACKAGE_SPEC"
                    fi
                fi
            else
                (__install_the_given_package "$PACKAGE_SPEC")
            fi
        done
    done

    #########################################################################################

    if [ "$REQUEST_TO_KEEP_SESSION_DIR" != 1 ] ; then
        rm -rf "$SESSION_DIR"
    fi
}

xbuilder() {
    XBUILDER="$SESSION_DIR/xbuilder"

    if [ ! -f "$XBUILDER" ] ; then
        wfetch 'https://raw.githubusercontent.com/leleliu008/ppkg/refs/heads/master/xbuilder' -o "$XBUILDER" --no-buffer
    fi

    run sh "$XBUILDER" install "$@" --prefix="$NATIVE_PACKAGE_INSTALLED_ROOT" --download-dir="$NDKPKG_DOWNLOADS_DIR" --session-dir="$SESSION_DIR/native"
}

__get_NATIVE_PLATFORM_TRIPLE() {
    case $NATIVE_PLATFORM_TYPE in
        macos)
            NATIVE_PLATFORM_VERS_MAJOR="${NATIVE_PLATFORM_VERS%%.*}"

            if [ "$NATIVE_PLATFORM_VERS_MAJOR" -le 10 ] ; then
                X='10.15'
            elif [ "$NATIVE_PLATFORM_VERS_MAJOR" -ge 15 ] ; then
                X='15.0'
            else
                X="$NATIVE_PLATFORM_VERS_MAJOR.0"
            fi

            NATIVE_PLATFORM_TRIPLE="macos-$X-$NATIVE_PLATFORM_ARCH"
            ;;
        linux)
            NATIVE_PLATFORM_TRIPLE="linux-$NATIVE_PLATFORM_LIBC-$NATIVE_PLATFORM_ARCH"
    esac

    printf '%s\n' "$NATIVE_PLATFORM_TRIPLE"
}

__install_the_given_package_onexit() {
    is_package_installed "$PACKAGE_SPEC" || {
        abort 1 "package installation failure: $PACKAGE_SPEC\n"

        if [ -n "$PACKAGE_WORKING_DIR"] ; then
            printf '%b\n' "${COLOR_RED}you would probably want to figure out want had happeded, please change to the working directory: $PACKAGE_WORKING_DIR${COLOR_OFF}"
        fi
    }
}

# Note: this function must run in a subshell
__install_the_given_package() {
    printf '%b\n' "${COLOR_PURPLE}>>> Installation ${COLOR_OFF}${COLOR_GREEN}${1}${COLOR_OFF}${COLOR_PURPLE} start${COLOR_OFF}"

    unset PACKAGE_WORKING_DIR

    trap  __install_the_given_package_onexit EXIT

    case $TARGET_PLATFORM_ARCH in
        armeabi-v7a)
            TARGET_PLATFORM_ABI='armeabi-v7a'
            TARGET_PLATFORM_ARCH='armv7a'
            TARGET_PLATFORM_NBIT=32
            ;;
        arm64-v8a)
            TARGET_PLATFORM_ABI='arm64-v8a'
            TARGET_PLATFORM_ARCH='aarch64'
            TARGET_PLATFORM_NBIT=64
            ;;
        x86)
            TARGET_PLATFORM_ABI='x86'
            TARGET_PLATFORM_ARCH='i686'
            TARGET_PLATFORM_NBIT=32
            ;;
        x86_64)
            TARGET_PLATFORM_ABI='x86_64'
            TARGET_PLATFORM_ARCH='x86_64'
            TARGET_PLATFORM_NBIT=64
            ;;
        *)  abort 1 "unsupported target arch: $TARGET_PLATFORM_ARCH"
    esac

    #########################################################################################

    if [ "$TARGET_PLATFORM_NBIT" -eq 64 ] ; then
        ANDROID_DYNAMIC_LINKER_PATH='/system/bin/linker64'
    else
        ANDROID_DYNAMIC_LINKER_PATH='/system/bin/linker'
    fi

    #########################################################################################

    if [ "$LOG_LEVEL" -ge "$LOG_LEVEL_VERBOSE" ] ; then
        cat <<EOF
      TIMESTAMP_UNIX = $TIMESTAMP_UNIX

NATIVE_PLATFORM_KIND = $NATIVE_PLATFORM_KIND
NATIVE_PLATFORM_TYPE = $NATIVE_PLATFORM_TYPE
NATIVE_PLATFORM_CODE = $NATIVE_PLATFORM_CODE
NATIVE_PLATFORM_NAME = $NATIVE_PLATFORM_NAME
NATIVE_PLATFORM_VERS = $NATIVE_PLATFORM_VERS
NATIVE_PLATFORM_ARCH = $NATIVE_PLATFORM_ARCH
NATIVE_PLATFORM_NCPU = $NATIVE_PLATFORM_NCPU
NATIVE_PLATFORM_LIBC = $NATIVE_PLATFORM_LIBC
NATIVE_PLATFORM_EUID = $NATIVE_PLATFORM_EUID
NATIVE_PLATFORM_EGID = $NATIVE_PLATFORM_EGID

TARGET_PLATFORM_VERS = $TARGET_PLATFORM_VERS
TARGET_PLATFORM_ARCH = $TARGET_PLATFORM_ARCH
TARGET_PLATFORM_ABI  = $TARGET_PLATFORM_ABI

NDKPKG_ARG0          = $NDKPKG_ARG0
NDKPKG_ARG1          = $NDKPKG_ARG1
NDKPKG_ARGV          = $NDKPKG_ARGV
NDKPKG_PATH          = $NDKPKG_PATH
NDKPKG_HOME          = $NDKPKG_HOME
NDKPKG_VERSION       = $NDKPKG_VERSION
NDKPKG_URL_TRANSFORM = $NDKPKG_URL_TRANSFORM

           LOG_LEVEL = $LOG_LEVEL
             PROFILE = $PROFILE

       ENABLE_CCACHE = $ENABLE_CCACHE
REQUEST_TO_KEEP_SESSION_DIR = $REQUEST_TO_KEEP_SESSION_DIR
REQUEST_TO_EXPORT_COMPILE_COMMANDS_JSON = $REQUEST_TO_EXPORT_COMPILE_COMMANDS_JSON
REQUEST_TO_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE = $REQUEST_TO_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE
EOF
    fi

    #########################################################################################

    step "load formula"

    PACKAGE_FORMULA_FILEPATH="$SESSION_DIR/$PACKAGE_NAME.yml"

    if [ "$DUMP_FORMULA" = 1 ] ; then
        bat --language=yaml --paging=never "$PACKAGE_FORMULA_FILEPATH"
    fi

    __load_formula_of_the_given_package "$PACKAGE_NAME" "$PACKAGE_FORMULA_FILEPATH"

    #########################################################################################

    unset PACKAGE_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE

    if [ "$REQUEST_TO_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE" = 1 ] ; then
        if [ "$PACKAGE_USE_BSYSTEM_GO" = 1 ] ; then
            note "You are requesting to create fully statically linked executables, but package '$PACKAGE_NAME' is a golang project and not supposed to create fully statically linked executables, so we will downgrade to create mostly statically linked executables. For more details, please read https://github.com/golang/go/issues/59942\n"
        else
            case $PACKAGE_PKGTYPE in
                exe)
                    PACKAGE_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE=1

                    if [ "$TARGET_PLATFORM_VERS" -ne "$ANDROID_NDK_SUPPORTED_MAX_SDK_API_LEVEL" ] ; then
                        abort 1 "You are requesting to create fully statically linked executables, but NDK only provide the libc.a for the latest Android API level $ANDROID_NDK_SUPPORTED_MAX_SDK_API_LEVEL. That's to say you're supposed to use Android API level $ANDROID_NDK_SUPPORTED_MAX_SDK_API_LEVEL to compile with this package. For more details, please read https://github.com/android/ndk/issues/2017"
                    fi
                    ;;
                pie)
                    note "You are requesting to create fully statically linked executables, but package '$PACKAGE_NAME' is supposed to create dynamically linked executables, so we will downgrade to create mostly statically linked executables."
            esac
        fi
    fi

    #########################################################################################

    if [ "$PACKAGE_PKGTYPE" != lib ] ; then
        export PACKAGE_CREATE_MOSTLY_STATICALLY_LINKED_EXECUTABLE=1
    else
        unset  PACKAGE_CREATE_MOSTLY_STATICALLY_LINKED_EXECUTABLE
    fi

    #########################################################################################

    if [ "$PACKAGE_PARALLEL" = 1 ] ; then
        BUILD_NJOBS="$NATIVE_PLATFORM_NCPU"
    else
        BUILD_NJOBS=1
    fi

    if [ -z "$PACKAGE_API_MIN" ] ; then
        PACKAGE_API_MIN="$ANDROID_NDK_SUPPORTED_MIN_SDK_API_LEVEL"
    fi

    if [ "$LOG_LEVEL" -ge "$LOG_LEVEL_VERBOSE" ] ; then
        cat <<EOF
supported min sdk api level: $PACKAGE_API_MIN
requested min sdk api level: $TARGET_PLATFORM_VERS
EOF
    fi

    if [ "$PACKAGE_API_MIN" -gt "$TARGET_PLATFORM_VERS" ] ; then
        abort 1 "package installation failure: '$1'\n    supported min sdk api level: $PACKAGE_API_MIN\n    requested min sdk api level: $TARGET_PLATFORM_VERS"
    fi

    #########################################################################################

    PACKAGE_INSTALL_UTS=
    PACKAGE_INSTALL_UTS="$(date +%s)"

    PACKAGE_INSTALL_SHA=
    PACKAGE_INSTALL_SHA="$(
{
    sha256sum <<EOF
$PACKAGE_SPEC:$$:$PACKAGE_INSTALL_UTS
EOF
} | cut -d ' ' -f1)"

    #########################################################################################

    PACKAGE_WORKING_DIR="$SESSION_DIR/$PACKAGE_SPEC"
    PACKAGE_BCACHED_DIR="$PACKAGE_WORKING_DIR/src/_"
    PACKAGE_INSTALL_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$TARGET_PLATFORM_SPEC/$PACKAGE_INSTALL_SHA"

    if [ -z "$PACKAGE_BSCRIPT" ] ; then
        PACKAGE_BSCRIPT_DIR="$PACKAGE_WORKING_DIR/src"
    else
        PACKAGE_BSCRIPT_DIR="$PACKAGE_WORKING_DIR/src/$PACKAGE_BSCRIPT"
    fi

    #########################################################################################

    [ "$LOG_LEVEL" -ge "$LOG_LEVEL_VERBOSE" ] && {
        cat <<EOF
PACKAGE_WORKING_DIR = $PACKAGE_WORKING_DIR
PACKAGE_BSCRIPT_DIR = $PACKAGE_BSCRIPT_DIR
PACKAGE_BCACHED_DIR = $PACKAGE_BCACHED_DIR
PACKAGE_INSTALL_DIR = $PACKAGE_INSTALL_DIR
EOF
}

    #########################################################################################

    step "create and change to the working directory"

    run install -d "$PACKAGE_WORKING_DIR"

    run cd "$PACKAGE_WORKING_DIR"

    run install -d src/_ fix res bin lib include

    #########################################################################################

    PACKAGE_INSTALLING_SRC_DIR="$PACKAGE_WORKING_DIR/src"
    PACKAGE_INSTALLING_FIX_DIR="$PACKAGE_WORKING_DIR/fix"
    PACKAGE_INSTALLING_RES_DIR="$PACKAGE_WORKING_DIR/res"
    PACKAGE_INSTALLING_BIN_DIR="$PACKAGE_WORKING_DIR/bin"
    PACKAGE_INSTALLING_LIB_DIR="$PACKAGE_WORKING_DIR/lib"
    PACKAGE_INSTALLING_INC_DIR="$PACKAGE_WORKING_DIR/include"

    #########################################################################################

    [ -n "$PACKAGE_ONSTART" ] && {
        step "onstart"

        eval "
onstart() {
$PACKAGE_ONSTART
}"
        onstart
    }

    #########################################################################################

    TOOLCHAIN_CONFIG_TARGET="$SESSION_DIR/toolchain-target.sh"

    if [ ! -f "$TOOLCHAIN_CONFIG_TARGET" ] ; then
        println_android_ndk_info > "$TOOLCHAIN_CONFIG_TARGET"
    fi

    #########################################################################################

    TOOLCHAIN_CONFIG_NATIVE="$SESSION_DIR/toolchain-native.sh"

    if [ -f "$TOOLCHAIN_CONFIG_NATIVE" ] ; then
        .   "$TOOLCHAIN_CONFIG_NATIVE"
    else
        step "locate C/C++ toolchain for native build"

        if [ "$NATIVE_PLATFORM_KIND" = 'darwin' ] ; then
                 CC_FOR_BUILD="$(xcrun --sdk macosx --find clang)"
               OBJC_FOR_BUILD="$(xcrun --sdk macosx --find clang)"
                CXX_FOR_BUILD="$(xcrun --sdk macosx --find clang++)"
                 AS_FOR_BUILD="$(xcrun --sdk macosx --find as)"
                 AR_FOR_BUILD="$(xcrun --sdk macosx --find ar)"
             RANLIB_FOR_BUILD="$(xcrun --sdk macosx --find ranlib)"
                 LD_FOR_BUILD="$(xcrun --sdk macosx --find ld)"
                 NM_FOR_BUILD="$(xcrun --sdk macosx --find nm)"
               SIZE_FOR_BUILD="$(xcrun --sdk macosx --find size)"
              STRIP_FOR_BUILD="$(xcrun --sdk macosx --find strip)"
            STRINGS_FOR_BUILD="$(xcrun --sdk macosx --find strings)"
            OBJDUMP_FOR_BUILD="$(xcrun --sdk macosx --find objdump)"
            SYSROOT_FOR_BUILD="$(xcrun --sdk macosx --show-sdk-path)"

            CCFLAGS_FOR_BUILD="-isysroot $SYSROOT_FOR_BUILD -mmacosx-version-min=$NATIVE_PLATFORM_VERS -arch $NATIVE_PLATFORM_ARCH -Qunused-arguments"
            LDFLAGS_FOR_BUILD='-Wl,-search_paths_first'
        else
                CXX_FOR_BUILD="$(command -v g++ || command -v clang++ || command -v c++)" || __install_gcc_via_syspm
                CXX_FOR_BUILD="$(command -v g++ || command -v clang++ || command -v c++)" || abort 1 "C++ Compiler for native not found."
                 CC_FOR_BUILD="$(command -v gcc || command -v clang   || command -v cc)"  || abort 1 "C Compiler for native not found."
               OBJC_FOR_BUILD="$(command -v gcc || command -v clang   || command -v cc)"  || abort 1 "C Compiler for native not found."
                 AS_FOR_BUILD="$(command -v as)"      || abort 1 "command not found: as"
                 AR_FOR_BUILD="$(command -v ar)"      || abort 1 "command not found: ar"
             RANLIB_FOR_BUILD="$(command -v ranlib)"  || abort 1 "command not found: ranlib"
                 LD_FOR_BUILD="$(command -v ld)"      || abort 1 "command not found: ld"
                 NM_FOR_BUILD="$(command -v nm)"      || abort 1 "command not found: nm"
               SIZE_FOR_BUILD="$(command -v size)"    || abort 1 "command not found: size"
              STRIP_FOR_BUILD="$(command -v strip)"   || abort 1 "command not found: strip"
            STRINGS_FOR_BUILD="$(command -v strings)" || abort 1 "command not found: strings"
            OBJDUMP_FOR_BUILD="$(command -v objdump)" || abort 1 "command not found: objdump"
            OBJCOPY_FOR_BUILD="$(command -v objcopy)" || abort 1 "command not found: objcopy"
            READELF_FOR_BUILD="$(command -v readelf)" || abort 1 "command not found: readelf"

            CCFLAGS_FOR_BUILD="-fPIC -fno-common"
            LDFLAGS_FOR_BUILD="-Wl,--as-needed -Wl,-z,muldefs -Wl,--allow-multiple-definition"
            #LDFLAGS_FOR_BUILD="-Wl,--as-needed -Wl,-z,muldefs -Wl,--allow-multiple-definition -fno-use-linker-plugin"
            # https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html
        fi

        if [ "$DEBUG_CC" = 1 ] ; then
            CCFLAGS_FOR_BUILD="$CCFLAGS_FOR_BUILD -v"
        fi

        if [ "$DEBUG_LD" = 1 ] ; then
            LDFLAGS_FOR_BUILD="$LDFLAGS_FOR_BUILD -Wl,-v"
        fi

        if [ "$PROFILE" = release ] ; then
            CCFLAGS_FOR_BUILD="$CCFLAGS_FOR_BUILD -Os"
            LDFLAGS_FOR_BUILD="$LDFLAGS_FOR_BUILD -Wl,-S"
        fi

        cat > "$TOOLCHAIN_CONFIG_NATIVE" <<EOF
     CC_FOR_BUILD='$CC_FOR_BUILD'
   OBJC_FOR_BUILD='$OBJC_FOR_BUILD'
    CXX_FOR_BUILD='$CXX_FOR_BUILD'
     AS_FOR_BUILD='$AS_FOR_BUILD'
     AR_FOR_BUILD='$AR_FOR_BUILD'
 RANLIB_FOR_BUILD='$RANLIB_FOR_BUILD'
     LD_FOR_BUILD='$LD_FOR_BUILD'
     NM_FOR_BUILD='$NM_FOR_BUILD'
   SIZE_FOR_BUILD='$SIZE_FOR_BUILD'
  STRIP_FOR_BUILD='$STRIP_FOR_BUILD'
STRINGS_FOR_BUILD='$STRINGS_FOR_BUILD'
OBJDUMP_FOR_BUILD='$OBJDUMP_FOR_BUILD'
OBJCOPY_FOR_BUILD='$OBJCOPY_FOR_BUILD'
READELF_FOR_BUILD='$READELF_FOR_BUILD'
SYSROOT_FOR_BUILD='$SYSROOT_FOR_BUILD'

CCFLAGS_FOR_BUILD='$CCFLAGS_FOR_BUILD'
LDFLAGS_FOR_BUILD='$LDFLAGS_FOR_BUILD'
EOF
    fi

    export PROXIED_CC_FOR_BUILD="$CC_FOR_BUILD"
    export PROXIED_CXX_FOR_BUILD="$CXX_FOR_BUILD"
    export PROXIED_OBJC_FOR_BUILD="$OBJC_FOR_BUILD"

    export CCFLAGS_FOR_BUILD

    CFLAGS_FOR_BUILD="$CCFLAGS_FOR_BUILD"

     CC_FOR_BUILD="$NDKPKG_CORE_DIR/wrapper-native-cc"
   OBJC_FOR_BUILD="$NDKPKG_CORE_DIR/wrapper-native-objc"
    CXX_FOR_BUILD="$NDKPKG_CORE_DIR/wrapper-native-c++"
    CPP_FOR_BUILD="$CC_FOR_BUILD -E"

    #########################################################################################

    for TOOL in CC OBJC CXX CPP AS AR RANLIB LD NM STRIP SIZE STRINGS OBJDUMP OBJCOPY READELF SYSROOT CFLAGS OBJCFLAGS CXXFLAGS CPPFLAGS LDFLAGS
    do
        export "${TOOL}_FOR_BUILD"
        eval export "$TOOL=\"\$${TOOL}_FOR_BUILD\""
    done

    #########################################################################################

    PKG_CONFIG_PATH_FOR_BUILD="$NDKPKG_CORE_DIR/lib"

    #########################################################################################

    if [ "$PACKAGE_USE_BSYSTEM_CMAKE" = 1 ] ; then
        # https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html#manual:cmake-env-variables(7)

        unset CMAKE_APPBUNDLE_PATH
        unset CMAKE_FRAMEWORK_PATH
        unset CMAKE_PROGRAM_PATH
        unset CMAKE_INCLUDE_PATH
        unset CMAKE_LIBRARY_PATH
        unset CMAKE_PREFIX_PATH
        unset CMAKE_MAXIMUM_RECURSION_DEPTH
        unset CMAKE_APPLE_SILICON_PROCESSOR
        unset CMAKE_BUILD_PARALLEL_LEVEL
        unset CMAKE_BUILD_TYPE
        unset CMAKE_CONFIGURATION_TYPES
        unset CMAKE_CONFIG_TYPE
        unset CMAKE_EXPORT_COMPILE_COMMANDS
        unset CMAKE_GENERATOR
        unset CMAKE_GENERATOR_INSTANCE
        unset CMAKE_GENERATOR_PLATFORM
        unset CMAKE_GENERATOR_TOOLSET
        unset CMAKE_INSTALL_MODE
        unset CMAKE_C_COMPILER_LAUNCHER
        unset CMAKE_C_LINKER_LAUNCHER
        unset CMAKE_CXX_COMPILER_LAUNCHER
        unset CMAKE_CXX_LINKER_LAUNCHER
        unset CMAKE_MSVCIDE_RUN_PATH
        unset CMAKE_NO_VERBOSE
        unset CMAKE_OSX_ARCHITECTURES
        unset CMAKE_TOOLCHAIN_FILE
        unset DESTDIR
        unset VERBOSE
        unset CTEST_INTERACTIVE_DEBUG_MODE
        unset CTEST_OUTPUT_ON_FAILURE
        unset CTEST_PARALLEL_LEVEL
        unset CTEST_PROGRESS_OUTPUT
        unset CTEST_USE_LAUNCHERS_DEFAULT
        unset DASHBOARD_TEST_FROM_CTEST

        # https://cmake.org/cmake/help/latest/envvar/CMAKE_BUILD_PARALLEL_LEVEL.html
        export CMAKE_BUILD_PARALLEL_LEVEL="$BUILD_NJOBS"

        # https://cmake.org/cmake/help/latest/envvar/CMAKE_GENERATOR.html
        if [ "$PACKAGE_USE_BSYSTEM_NINJA" = 1 ] ; then
            export CMAKE_GENERATOR='Ninja'
        else
            export CMAKE_GENERATOR='Unix Makefiles'
        fi

        # https://cmake.org/cmake/help/latest/envvar/CMAKE_EXPORT_COMPILE_COMMANDS.html
        if [ "$REQUEST_TO_EXPORT_COMPILE_COMMANDS_JSON" = 1 ] ; then
            export CMAKE_EXPORT_COMPILE_COMMANDS=ON
        else
            export CMAKE_EXPORT_COMPILE_COMMANDS=OFF
        fi

        case $PROFILE in
            debug)   CMAKE_BUILD_TYPE=Debug   ;;
            release) CMAKE_BUILD_TYPE=Release ;;
        esac

        if [ "$VERBOSE_CMAKE" = 1 ] ; then
            CMAKE_VERBOSE_MAKEFILE=ON
            CMAKE_COLOR_MAKEFILE=ON
            CMAKE_INSTALL_MESSAGE=ALWAYS
        else
            CMAKE_VERBOSE_MAKEFILE=OFF
            CMAKE_COLOR_MAKEFILE=OFF
            CMAKE_INSTALL_MESSAGE=NEVER
        fi

        # https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_DEBUG_MODE.html
        if [ "$DEBUG_CMAKE" = 1 ] ; then
            CMAKE_FIND_DEBUG_MODE=ON
        else
            CMAKE_FIND_DEBUG_MODE=OFF
        fi
    fi

    #########################################################################################

    unset RECURSIVE_DEPENDENT_PACKAGE_NAMES

    [ -n "$PACKAGE_DEP_PKG" ] && {
        step "calculate dependency list of $1"

        unset PACKAGE_NAME_STACK

        for item in $PACKAGE_DEP_PKG
        do
            if [ -z "$item" ] ; then
                PACKAGE_NAME_STACK="$item"
            else
                PACKAGE_NAME_STACK="$PACKAGE_NAME_STACK;$item"
            fi
        done

        while [ -n "$PACKAGE_NAME_STACK" ]
        do
            case $PACKAGE_NAME_STACK in
                *\;*) TOPE="${PACKAGE_NAME_STACK##*;}" ; PACKAGE_NAME_STACK="${PACKAGE_NAME_STACK%;*}" ;;
                *)    TOPE="${PACKAGE_NAME_STACK}"     ; PACKAGE_NAME_STACK=
            esac

            RECURSIVE_DEPENDENT_PACKAGE_NAMES2="$TOPE"

            for item in $RECURSIVE_DEPENDENT_PACKAGE_NAMES
            do
                if [ "$item" != "$TOPE" ] ; then
                    RECURSIVE_DEPENDENT_PACKAGE_NAMES2="$RECURSIVE_DEPENDENT_PACKAGE_NAMES2 $item"
                fi
            done

            RECURSIVE_DEPENDENT_PACKAGE_NAMES="$RECURSIVE_DEPENDENT_PACKAGE_NAMES2"

            unset TOPE_UPPERCASE_UNDERSCORE
            TOPE_UPPERCASE_UNDERSCORE=$(printf '%s\n' "$TOPE" | tr '@+-.' '_' | tr a-z A-Z)

            for item in $(eval echo \$PACKAGE_DEP_PKG_"${TOPE_UPPERCASE_UNDERSCORE}")
            do
                if [ -z "$item" ] ; then
                    PACKAGE_NAME_STACK="$item"
                else
                    PACKAGE_NAME_STACK="$PACKAGE_NAME_STACK;$item"
                fi
            done
        done

        printf '%s\n' "$RECURSIVE_DEPENDENT_PACKAGE_NAMES"
    }

    #########################################################################################

    unset RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS

    [ -n "$PACKAGE_DEP_PKG" ] && {
        for DEPENDENT_PACKAGE_NAME in $RECURSIVE_DEPENDENT_PACKAGE_NAMES
        do
            DEPENDENT_PACKAGE_INSTALL_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$TARGET_PLATFORM_SPEC/$DEPENDENT_PACKAGE_NAME"

            if [ -z "$RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS" ] ; then
                RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS="$DEPENDENT_PACKAGE_INSTALL_DIR"
            else
                RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS="$RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS $DEPENDENT_PACKAGE_INSTALL_DIR"
            fi

            #########################################################################################

            DEPENDENT_PACKAGE_BINARY__DIR="$NATIVE_PACKAGE_INSTALLED_ROOT/$DEPENDENT_PACKAGE_NAME/bin"

            if [ -d  "$DEPENDENT_PACKAGE_BINARY__DIR" ] ; then
                PATH="$DEPENDENT_PACKAGE_BINARY__DIR:$PATH"
            fi

            DEPENDENT_PACKAGE_BINARY__DIR="$NATIVE_PACKAGE_INSTALLED_ROOT/$DEPENDENT_PACKAGE_NAME/sbin"

            if [ -d  "$DEPENDENT_PACKAGE_BINARY__DIR" ] ; then
                PATH="$DEPENDENT_PACKAGE_BINARY__DIR:$PATH"
            fi

            if [ -d          "$NATIVE_PACKAGE_INSTALLED_ROOT/$DEPENDENT_PACKAGE_NAME/share/aclocal" ] ; then
                ACLOCAL_PATH="$NATIVE_PACKAGE_INSTALLED_ROOT/$DEPENDENT_PACKAGE_NAME/share/aclocal:$ACLOCAL_PATH"
            fi
        done
    }

    #########################################################################################

    [ -n "$PACKAGE_DEP_PKG" ] && {
        step "generate  dependency tree of $1"

        unset DOT_CONTENT
        unset D2__CONTENT

        STACK="$PACKAGE_NAME"

        while [ -n "$STACK" ]
        do
            case $STACK in
                *\;*) TOPE="${STACK##*;}" ; STACK="${STACK%;*}" ;;
                *)    TOPE="${STACK}"     ; STACK=
            esac

            unset TOPE_UPPERCASE_UNDERSCORE
            TOPE_UPPERCASE_UNDERSCORE=$(printf '%s\n' "$TOPE" | tr '@+-.' '_' | tr a-z A-Z)

            TOPE_DEP_PKG="$(eval echo \$PACKAGE_DEP_PKG_"${TOPE_UPPERCASE_UNDERSCORE}")"

            if [ -n "$TOPE_DEP_PKG" ] ; then
                unset X; X="$(printf '"%s" ' $TOPE_DEP_PKG)"
                unset Y; Y="$(printf '    "%s" -> { %s}\n' "$TOPE" "$X")"

                if [ -z "$DOT_CONTENT" ] ; then
                    DOT_CONTENT="$Y"
                else
                    DOT_CONTENT="$(printf '%s\n%s\n' "$DOT_CONTENT" "$Y")"
                fi
            fi

            for item in $TOPE_DEP_PKG
            do
                if [ -z "$D2__CONTENT" ] ; then
                    D2__CONTENT="$TOPE -> $item"
                else
                    D2__CONTENT="$D2__CONTENT
$TOPE -> $item"
                fi

                if [ -z "$item" ] ; then
                    STACK="$item"
                else
                    STACK="$STACK;$item"
                fi
            done
        done

        DOT_CONTENT="digraph G {
$DOT_CONTENT
}"

        #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

        PACKAGE_DEPENDENCY_GRAPH_FILEPATH_D2_="$PACKAGE_WORKING_DIR/dependencies.d2"
        PACKAGE_DEPENDENCY_GRAPH_FILEPATH_DOT="$PACKAGE_WORKING_DIR/dependencies.dot"
        PACKAGE_DEPENDENCY_GRAPH_FILEPATH_BOX="$PACKAGE_WORKING_DIR/dependencies.box"

        #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

        printf '%s\n' "$D2__CONTENT" > "$PACKAGE_DEPENDENCY_GRAPH_FILEPATH_D2_"
        printf '%s\n' "$DOT_CONTENT" > "$PACKAGE_DEPENDENCY_GRAPH_FILEPATH_DOT"

        #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

        # https://github.com/ggerganov/dot-to-ascii
        curl \
            -o "$PACKAGE_DEPENDENCY_GRAPH_FILEPATH_BOX" \
            -s \
            -G \
            --data-urlencode "boxart=1" \
            --data-urlencode "src=$DOT_CONTENT" \
            "https://dot-to-ascii.ggerganov.com/dot-to-ascii.php" || true

        #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

        DOT="$(command -v dot || command -v dot_static || true)"

        if [ -n "$DOT" ] ; then
            run "$DOT" -Tsvg -o "$PACKAGE_WORKING_DIR/dependencies.svg" "$PACKAGE_DEPENDENCY_GRAPH_FILEPATH_DOT" || true
        else
            D2="$(command -v d2 || true)"

            if [ -n "$D2" ] ; then
                run "$D2" "$PACKAGE_DEPENDENCY_GRAPH_FILEPATH_D2_" "$PACKAGE_WORKING_DIR/dependencies.svg"
            fi
        fi

        #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

        if [ -f "$PACKAGE_DEPENDENCY_GRAPH_FILEPATH_BOX" ] ; then
            cat "$PACKAGE_DEPENDENCY_GRAPH_FILEPATH_BOX"
        else
            cat "$PACKAGE_DEPENDENCY_GRAPH_FILEPATH_DOT"
        fi
    }

    #########################################################################################

    if [ "$PACKAGE_USE_BSYSTEM_GMAKE" = 1 ] && [ "$REQUEST_TO_EXPORT_COMPILE_COMMANDS_JSON" = 1 ] && [ "$BEAR_ENABLED" = 1 ] ; then
        PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP bear"
    fi

    if [ "$ENABLE_CCACHE" = 1 ] ; then
        PACKAGE_DEP_UPP="$PACKAGE_DEP_UPP ccache"
    fi

    #########################################################################################

    unset PACKAGE_DEP_UPP_LIBZ
    unset PACKAGE_DEP_UPP_LIBOPENSSL
    unset PACKAGE_DEP_UPP_PYTHON3
    unset PACKAGE_DEP_UPP_RUBY

    # these native packages are not relocatable and would be fetched to a fixed location /opt/non-relocatable-*
    unset PACKAGE_DEP_UPP_T2

    # these native packages would be built from source locally
    unset PACKAGE_DEP_XBUILDER

    #########################################################################################

    if [ "$TERMUX" = 1 ] ; then
        TERMUX_PKGS='pkg-config patchelf tree'

        for item in $PACKAGE_DEP_UPP
        do
            case $item in
                gm4)      TERMUX_PKGS="$TERMUX_PKGS m4"         ;;
                gsed)     TERMUX_PKGS="$TERMUX_PKGS sed"        ;;
                gtar)     TERMUX_PKGS="$TERMUX_PKGS tar"        ;;
                gmake)    TERMUX_PKGS="$TERMUX_PKGS make"       ;;
                xz)       TERMUX_PKGS="$TERMUX_PKGS xz-utils"   ;;
                bsdtar)   TERMUX_PKGS="$TERMUX_PKGS libarchive" ;;
                python3)  TERMUX_PKGS="$TERMUX_PKGS python"     ;;
                libz)     TERMUX_PKGS="$TERMUX_PKGS zlib"       ;;
                *)        TERMUX_PKGS="$TERMUX_PKGS $item"      ;;
            esac
        done

        step "install needed packages via pkg"

        run pkg install -y $TERMUX_PKGS
    else
        step "install needed packages via uppm"

        run "$UPPM" about
        run "$UPPM" update

        unset UPPM_INSTALL_ARGS

        if [ "$DUMP_UPPM" = 1 ] ; then
            UPPM_INSTALL_ARGS=-v
        fi

        for UPPM_PACKAGE_NAME in $PACKAGE_DEP_UPP pkg-config patchelf tree
        do
            case $UPPM_PACKAGE_NAME in
                libz)
                    PACKAGE_DEP_UPP_LIBZ=1
                    PACKAGE_DEP_XBUILDER="$PACKAGE_DEP_XBUILDER libz"
                    continue
                    ;;
                libopenssl)
                    PACKAGE_DEP_UPP_LIBOPENSSL=1
                    PACKAGE_DEP_XBUILDER="$PACKAGE_DEP_XBUILDER libopenssl"
                    continue
                    ;;
                autotools)
                    PACKAGE_DEP_UPP_T2="$PACKAGE_DEP_UPP_T2 perl autoconf automake"
                    UPPM_PACKAGE_NAME=gm4
                    ;;
                perl|autoconf|automake|libtool|texinfo|help2man|intltool)
                    PACKAGE_DEP_UPP_T2="$PACKAGE_DEP_UPP_T2 $UPPM_PACKAGE_NAME"
                    continue
                    ;;
                python3)
                    PACKAGE_DEP_UPP_PYTHON3=1
                    continue
                    ;;
                ruby)
                    PACKAGE_DEP_UPP_RUBY=1
                    continue
                    ;;
            esac

            run "$UPPM" install "$UPPM_PACKAGE_NAME" $UPPM_INSTALL_ARGS

            UPPM_PACKAGE_INSTALLED_DIR="$UPPM_HOME/installed/$UPPM_PACKAGE_NAME"

            if [ -d  "$UPPM_PACKAGE_INSTALLED_DIR/bin" ] ; then
                PATH="$UPPM_PACKAGE_INSTALLED_DIR/bin:$PATH"
            fi

            if [ -d  "$UPPM_PACKAGE_INSTALLED_DIR/sbin" ] ; then
                PATH="$UPPM_PACKAGE_INSTALLED_DIR/sbin:$PATH"
            fi

            if [ -d          "$UPPM_PACKAGE_INSTALLED_DIR/share/aclocal" ] ; then
                ACLOCAL_PATH="$UPPM_PACKAGE_INSTALLED_DIR/share/aclocal:$ACLOCAL_PATH"
            fi

            case $UPPM_PACKAGE_NAME in
                swig)
                    # https://www.swig.org/Doc4.0/Library.html
                    X="$(ls $UPPM_PACKAGE_INSTALLED_DIR/share/swig/*/swig.swg)"
                    export SWIG_LIB="${X%/*}"
                    ;;
                file)
                    export MAGIC="$UPPM_PACKAGE_INSTALLED_DIR/share/misc/magic.mgc"
                    ;;
                docbook-xsl)
                    # http://xmlsoft.org/xslt/xsltproc.html
                    export XML_CATALOG_FILES="$UPPM_PACKAGE_INSTALLED_DIR/catalog.xml"
                    printf '%s\n' "XML_CATALOG_FILES=$XML_CATALOG_FILES"
            esac
        done
    fi

    #########################################################################################

    [ -n "$PACKAGE_DEP_UPP_T2" ] && {
        if [ "$GITHUB_ACTIONS" = true ] ; then
            NON_RELOCATEABLE_BINARY_PACKAGE_NAME="non-relocatable-binary-packages-2024.10.27-$(__get_NATIVE_PLATFORM_TRIPLE)"
            NON_RELOCATEABLE_BINARY_PACKAGE_INSTALL_DIR="/opt/$NON_RELOCATEABLE_BINARY_PACKAGE_NAME"

            if [ ! -f "$NON_RELOCATEABLE_BINARY_PACKAGE_INSTALL_DIR/ok" ] ; then
                wfetch "https://github.com/leleliu008/non-relocatable-binary-packages/releases/download/2024.10.27/$NON_RELOCATEABLE_BINARY_PACKAGE_NAME.tar.xz"
                run $sudo install -d -g `id -g` -o `id -u` "$NON_RELOCATEABLE_BINARY_PACKAGE_INSTALL_DIR"
                run bsdtar xPf "$NON_RELOCATEABLE_BINARY_PACKAGE_NAME.tar.xz"
                run touch "$NON_RELOCATEABLE_BINARY_PACKAGE_INSTALL_DIR/ok"
            fi

            export PATH="$NON_RELOCATEABLE_BINARY_PACKAGE_INSTALL_DIR/bin:$PATH"
        else
            PACKAGE_DEP_XBUILDER="$PACKAGE_DEP_XBUILDER $PACKAGE_DEP_UPP_T2"
        fi
    }

    #########################################################################################

    [ -n "$PACKAGE_DEP_XBUILDER" ] && {
        run "$UPPM" install gmake

        export PATH="$UPPM_HOME/installed/gmake/bin:$PATH"

        xbuilder "$PACKAGE_DEP_XBUILDER"

        for NATIVE_PACKAGE_NAME in $PACKAGE_DEP_XBUILDER
        do
            NATIVE_PACKAGE_INSTALLED_DIR="$NATIVE_PACKAGE_INSTALLED_ROOT/$NATIVE_PACKAGE_NAME"

            if [ -d  "$NATIVE_PACKAGE_INSTALLED_DIR/include" ] ; then
                CPPFLAGS_FOR_BUILD="$CPPFLAGS_FOR_BUILD -I$NATIVE_PACKAGE_INSTALLED_DIR/include"
            fi

            if [ -d  "$NATIVE_PACKAGE_INSTALLED_DIR/lib" ] ; then
                LDFLAGS_FOR_BUILD="$LDFLAGS_FOR_BUILD -L$NATIVE_PACKAGE_INSTALLED_DIR/lib -Wl,-rpath,$NATIVE_PACKAGE_INSTALLED_DIR/lib"
            fi

            if [ -d  "$NATIVE_PACKAGE_INSTALLED_DIR/bin" ] ; then
                PATH="$NATIVE_PACKAGE_INSTALLED_DIR/bin:$PATH"
            fi

            if [ -d  "$NATIVE_PACKAGE_INSTALLED_DIR/sbin" ] ; then
                PATH="$NATIVE_PACKAGE_INSTALLED_DIR/sbin:$PATH"
            fi

            if [ -d          "$NATIVE_PACKAGE_INSTALLED_DIR/share/aclocal" ] ; then
                ACLOCAL_PATH="$NATIVE_PACKAGE_INSTALLED_DIR/share/aclocal:$ACLOCAL_PATH"
            fi
        done
    }

    #########################################################################################

    [ "$PACKAGE_DEP_UPP_RUBY" = 1 ] && {
        RUBY_DISTRIBUTION_FILENAME_PREFIX="ruby-3.3.6-$(__get_NATIVE_PLATFORM_TRIPLE)"
        RUBY_DISTRIBUTION_INSTALL_DIR="$NATIVE_PACKAGE_INSTALLED_ROOT/ruby"

        if [ ! -f "$RUBY_DISTRIBUTION_INSTALL_DIR/ok" ] ; then
            wfetch "https://github.com/leleliu008/ruby-distribution/releases/download/3.3.6/$RUBY_DISTRIBUTION_FILENAME_PREFIX.tar.xz"
            run install -d "$RUBY_DISTRIBUTION_INSTALL_DIR"
            run bsdtar xf  "$RUBY_DISTRIBUTION_FILENAME_PREFIX.tar.xz" -C "$RUBY_DISTRIBUTION_INSTALL_DIR" --strip-components=1
            run touch "$RUBY_DISTRIBUTION_INSTALL_DIR/ok"
        fi

        export PATH="$RUBY_DISTRIBUTION_INSTALL_DIR/bin:$PATH"
    }

    #########################################################################################

    [ "$PACKAGE_DEP_UPP_PYTHON3" = 1 ] && {
        PYTHON3_DISTRIBUTION_FILENAME_PREFIX="python-distribution-2024.10.27-$(__get_NATIVE_PLATFORM_TRIPLE)"
        PYTHON3_DISTRIBUTION_INSTALL_DIR="$NATIVE_PACKAGE_INSTALLED_ROOT/python3"

        if [ ! -f "$PYTHON3_DISTRIBUTION_INSTALL_DIR/ok" ] ; then
            wfetch "https://github.com/leleliu008/python-distribution/releases/download/2024.10.27/$PYTHON3_DISTRIBUTION_FILENAME_PREFIX.tar.xz"
            run install -d "$PYTHON3_DISTRIBUTION_INSTALL_DIR"
            run bsdtar xf  "$PYTHON3_DISTRIBUTION_FILENAME_PREFIX.tar.xz" -C "$PYTHON3_DISTRIBUTION_INSTALL_DIR" --strip-components=1
            run touch "$PYTHON3_DISTRIBUTION_INSTALL_DIR/ok"
        fi

        export PATH="$PYTHON3_DISTRIBUTION_INSTALL_DIR/bin:$PATH"
    }

    #########################################################################################

    [ -n "$PACKAGE_DEP_PYM" ] && {
        step "install needed python packages via pip"

        PYTHON3="$(command -v python3)" || abort 1 "command not found: python3"

        run "$PYTHON3" --version
        run "$PYTHON3" -m pip install --upgrade pip
        run "$PYTHON3" -m pip install --upgrade "$PACKAGE_DEP_PYM"
    }

    #########################################################################################

    # cpan use the C/C++ compiler same as perl was built with
    # so make sure C/C++ compiler be found before reaching here

    [ -n "$PACKAGE_DEP_PLM" ] && {
        step "install needed perl modules via cpan"

        unset PACKAGE_DEP_PLM_T1
        unset PACKAGE_DEP_PLM_T2

        for item in $PACKAGE_DEP_PLM
        do
            if [ "$item" = 'XML::Parser' ] ; then
                PACKAGE_DEP_PLM_T1='XML::Parser'
            else
                PACKAGE_DEP_PLM_T2="$PACKAGE_DEP_PLM_T2 $item"
            fi
        done

        if [ -n "$PACKAGE_DEP_PLM_T1" ] ; then
            xbuilder perl_XML_Parser
        fi

        if [ -n "$PACKAGE_DEP_PLM_T2" ] ; then
            PACKAGE_DEP_PLM_T2="${PACKAGE_DEP_PLM_T2# }"
        fi

        if [ -n "$PACKAGE_DEP_PLM_T2" ] ; then
            # Would you like to configure as much as possible automatically? [yes]
            # https://perldoc.perl.org/cpan#PERL_MM_USE_DEFAULT
            export PERL_MM_USE_DEFAULT=1
            run cpan "$PACKAGE_DEP_PLM_T2"
        fi

        if [ -d "$HOME/perl5/bin" ] ; then
            # cpan install to default local location
            bppend_to_PATH "$HOME/perl5/bin"
        fi
    }

    #########################################################################################

    [ "$PACKAGE_USE_BSYSTEM_CARGO" = 1 ] && {
        command -v rustup > /dev/null || {
            # https://www.rust-lang.org/tools/install
            note "${COLOR_GREEN}rustup cargo rustc${COLOR_OFF} ${COLOR_YELLOW}commands are required, but it was not found, let's install it.${COLOR_OFF}"

            wfetch 'https://sh.rustup.rs' -o rustup-init.sh --no-buffer

            run bash rustup-init.sh -y
        }
    }

    #########################################################################################

    step "locate needed tools"

    unset AUTORECONF
    unset AUTOCONF
    unset AUTOMAKE
    unset ACLOCAL
    unset PERL
    unset M4

    unset MESON
    unset CMAKE
    unset XMAKE
    unset GMAKE
    unset NINJA

    unset BEAR
    unset CCACHE
    unset PKG_CONFIG

    if [ "$PACKAGE_USE_BSYSTEM_AUTOGENSH" = 1 ] || [ "$PACKAGE_USE_BSYSTEM_AUTOTOOLS" = 1 ] ; then
        AUTORECONF=$(command -v autoreconf) || abort 1 "command not found: autoreconf"
        AUTOCONF=$(command -v autoconf)     || abort 1 "command not found: autoconf"
        AUTOMAKE=$(command -v automake)     || abort 1 "command not found: automake"
        ACLOCAL=$(command -v aclocal)       || abort 1 "command not found: aclocal"
        PERL=$(command -v perl)             || abort 1 "command not found: perl"
        M4=$(command -v m4)                 || abort 1 "command not found: m4"
    fi

    [ "$PACKAGE_USE_BSYSTEM_MESON" = 1 ] && {
        MESON=$(command -v meson) || abort 1 "command not found: meson"
    }

    [ "$PACKAGE_USE_BSYSTEM_CMAKE" = 1 ] && {
        CMAKE=$(command -v cmake) || abort 1 "command not found: cmake"
    }

    [ "$PACKAGE_USE_BSYSTEM_XMAKE" = 1 ] && {
        XMAKE=$(command -v xmake) || abort 1 "command not found: xmake"

        # error: Running xmake as root is extremely dangerous and no longer supported.
        # As xmake does not drop privileges on installation you would be giving all
        # build scripts full access to your system.
        # Or you can add `--root` option or XMAKE_ROOT=y to allow run as root temporarily.
        export XMAKE_ROOT=y
    }

    [ "$PACKAGE_USE_BSYSTEM_GMAKE" = 1 ] && {
        GMAKE=$(command -v gmake || command -v make) || abort 1 "command not found: gmake and make"
    }

    [ "$PACKAGE_USE_BSYSTEM_NINJA" = 1 ] && {
        NINJA=$(command -v ninja) || abort 1 "command not found: ninja"
    }

    [ "$PACKAGE_USE_BSYSTEM_CARGO" = 1 ] && {
        RUSTUP=$(command -v rustup) || abort 1 "command not found: rustup"
        CARGO=$(command -v cargo)   || abort 1 "command not found: cargo"
    }

    [ "$PACKAGE_USE_BSYSTEM_GO" = 1 ] && {
        GO=$(command -v go) || abort 1 "command not found: go"
    }

    [ "$ENABLE_CCACHE" = 1 ] && {
        CCACHE=$(command -v ccache) || abort 1 "command not found: ccache"
    }

    PKG_CONFIG=$(command -v pkg-config || command -v pkgconf) || abort 1 "command not found: pkg-config"

    unset  M4
    export M4="$(command -v m4 || true)"

    PATCHELF=$(command -v patchelf) || abort 1 "command not found: patchelf"

    TREE=$(command -v tree) || abort 1 "command not found: tree"

    #########################################################################################

    step "fetching resources"

    case $PACKAGE_SRC_URL in
        '')
            if [ -n "$PACKAGE_GIT_URL" ] ; then
                unset GIT_FETCH_URL

                if [ -z "$NDKPKG_URL_TRANSFORM" ] ; then
                    GIT_FETCH_URL="$PACKAGE_GIT_URL"
                else
                    GIT_FETCH_URL="$("$NDKPKG_URL_TRANSFORM" "$PACKAGE_GIT_URL")" || return 1
                fi

                if [ -z "$PACKAGE_GIT_SHA" ] ; then
                    if [ -z "$PACKAGE_GIT_REF" ] ; then
                        GIT_BRANCH_NAME=master
                        GIT_REF_SPEC="+HEAD:refs/remotes/origin/master"
                    else
                        GIT_BRANCH_NAME="$(basename "$PACKAGE_GIT_REF")"
                        GIT_REF_SPEC="+$PACKAGE_GIT_REF:refs/remotes/origin/$GIT_BRANCH_NAME"
                    fi
                else
                    GIT_BRANCH_NAME=master
                    GIT_REF_SPEC="+$PACKAGE_GIT_SHA:refs/remotes/origin/master"
                fi

                if [ -z "$PACKAGE_GIT_NTH" ] ; then
                    PACKAGE_GIT_NTH=1
                fi

                if [ "$PACKAGE_GIT_NTH" -eq 0 ] ; then
                    if [ -f "$PACKAGE_SRC_FILEPATH/.git/shallow" ] ; then
                        GIT_FETCH_EXTRA_OPTIONS='--unshallow'
                    else
                        GIT_FETCH_EXTRA_OPTIONS=
                    fi
                else
                    GIT_FETCH_EXTRA_OPTIONS="--depth=$PACKAGE_GIT_NTH"
                fi

                run cd "$PACKAGE_INSTALLING_SRC_DIR"

                run git -c init.defaultBranch=master init
                run git remote add origin "$GIT_FETCH_URL"
                run git -c protocol.version=2 fetch --progress $GIT_FETCH_EXTRA_OPTIONS origin "$GIT_REF_SPEC"
                run git checkout --progress --force -B "$GIT_BRANCH_NAME" "refs/remotes/origin/$GIT_BRANCH_NAME"

                git_submodule_update_recursive
            fi
            ;;
        dir://*)
            note "$PACKAGE_SRC_URL is local path, no need to fetch."
            ;;
        file://*)
            note "$PACKAGE_SRC_URL is local path, no need to fetch."
            ;;
        *)  wfetch "$PACKAGE_SRC_URL" --uri="$PACKAGE_SRC_URI" --sha256="$PACKAGE_SRC_SHA" -o "$PACKAGE_SRC_FILEPATH"
    esac

    if [ -n    "$PACKAGE_FIX_URL" ] ; then
        wfetch "$PACKAGE_FIX_URL" --uri="$PACKAGE_FIX_URI" --sha256="$PACKAGE_FIX_SHA" -o "$PACKAGE_FIX_FILEPATH"
    fi

    if [ -n    "$PACKAGE_RES_URL" ] ; then
        wfetch "$PACKAGE_RES_URL" --uri="$PACKAGE_RES_URI" --sha256="$PACKAGE_RES_SHA" -o "$PACKAGE_RES_FILEPATH"
    fi

    #########################################################################################

    step "unpack/copy resources to proper location"

    if [ -n "$PACKAGE_SRC_FILEPATH" ] ; then
        case $PACKAGE_SRC_FILETYPE in
            .dir)
                if [ -d "$PACKAGE_SRC_FILEPATH" ] ; then
                    if [ -d "$PACKAGE_SRC_FILEPATH/.git" ] && command -v git > /dev/null ; then
                        PACKAGE_GIT_SHA=$(git -C "$PACKAGE_SRC_FILEPATH" rev-parse HEAD || true)
                    fi
                    run cp -r "$PACKAGE_SRC_FILEPATH/." "$PACKAGE_INSTALLING_SRC_DIR"
                else
                    abort 1 "src-url point to dir '$PACKAGE_SRC_FILEPATH' does not exist."
                fi
                ;;
            .git)
                if [ -z "$PACKAGE_GIT_SHA" ] ; then
                    PACKAGE_GIT_SHA="$(git rev-parse HEAD)"
                fi
                ;;
            .zip|.txz|.tgz|.tlz|.tbz2|.crate)
                run bsdtar xf "$PACKAGE_SRC_FILEPATH" -C "$PACKAGE_INSTALLING_SRC_DIR" --strip-components 1 --no-same-owner
                ;;
            *)  run cp "$PACKAGE_SRC_FILEPATH" "$PACKAGE_INSTALLING_SRC_DIR/"
        esac
    fi

    if [ -n "$PACKAGE_FIX_FILEPATH" ] ; then
        case $PACKAGE_FIX_FILETYPE in
            .zip|.txz|.tgz|.tlz|.tbz2|.crate)
                run bsdtar xf "$PACKAGE_FIX_FILEPATH" -C "$PACKAGE_INSTALLING_FIX_DIR" --strip-components 1 --no-same-owner
                ;;
            *)  run cp "$PACKAGE_FIX_FILEPATH" "$PACKAGE_INSTALLING_FIX_DIR/"
                printf '%s|%s\n' "$PACKAGE_FIX_FILENAME" "$PACKAGE_FIX_OPT" > "$PACKAGE_INSTALLING_FIX_DIR/index"
        esac
    fi

    if [ -n "$PACKAGE_RES_FILEPATH" ] ; then
        case $PACKAGE_RES_FILETYPE in
            .zip|.txz|.tgz|.tlz|.tbz2|.crate)
                run bsdtar xf "$PACKAGE_RES_FILEPATH" -C "$PACKAGE_INSTALLING_RES_DIR" --strip-components 1 --no-same-owner
                ;;
            *)  run cp "$PACKAGE_RES_FILEPATH" "$PACKAGE_INSTALLING_RES_DIR/"
        esac
    fi

    for LINE in $PACKAGE_PATCHES
    do
        SHA="$(printf '%s\n' "$LINE" | cut -d '|' -f1)"
        URL="$(printf '%s\n' "$LINE" | cut -d '|' -f2)"
        URI="$(printf '%s\n' "$LINE" | cut -d '|' -f3)"
        OPT="$(printf '%s\n' "$LINE" | cut -d '|' -f4)"

        FILETYPE="$(filetype_from_url "$URL")"
        FILENAME="$SHA$FILETYPE"
        FILEPATH="$NDKPKG_DOWNLOADS_DIR/$FILENAME"

        wfetch "$URL" --uri="$URI" --sha256="$SHA" -o "$FILEPATH"

        case $FILETYPE in
            .zip|.txz|.tgz|.tlz|.tbz2|.crate)
                run bsdtar xf "$FILEPATH" -C "$PACKAGE_INSTALLING_FIX_DIR" --strip-components 1 --no-same-owner
                ;;
            *)  run cp "$FILEPATH" "$PACKAGE_INSTALLING_FIX_DIR/"
                printf '%s|%s\n' "$FILENAME" "$OPT" >> "$PACKAGE_INSTALLING_FIX_DIR/index"
        esac
    done

    for LINE in $PACKAGE_RESLIST
    do
        SHA="$(printf '%s\n' "$LINE" | cut -d '|' -f1)"
        URL="$(printf '%s\n' "$LINE" | cut -d '|' -f2)"
        URI="$(printf '%s\n' "$LINE" | cut -d '|' -f3)"
        DIR="$(printf '%s\n' "$LINE" | cut -d '|' -f4)"
        LEV="$(printf '%s\n' "$LINE" | cut -d '|' -f5)"

        [ -z "$LEV" ] && LEV=1

        FILETYPE="$(filetype_from_url "$URL")"
        FILENAME="$SHA$FILETYPE"
        FILEPATH="$NDKPKG_DOWNLOADS_DIR/$FILENAME"

        wfetch "$URL" --uri="$URI" --sha256="$SHA" -o "$FILEPATH"

        if [ -z "$DIR" ] ; then
            DEST="$PACKAGE_INSTALLING_RES_DIR"
        else
            DEST="$PACKAGE_INSTALLING_RES_DIR/$DIR"
            run install -d "$DEST"
        fi

        case $FILETYPE in
            .zip|.txz|.tgz|.tlz|.tbz2|.crate)
                run bsdtar xf "$FILEPATH" -C "$DEST" --strip-components "$LEV" --no-same-owner
                ;;
            *)  run cp "$FILEPATH" "$DEST/"
        esac
    done

    #########################################################################################

    [ "$LOG_LEVEL" -ge "$LOG_LEVEL_VERBOSE" ] && {
        step "tree files of the installing top directory"
        run tree --dirsfirst -L 2 "$PACKAGE_WORKING_DIR"

        step "list files of the installing src directory"
        run ls -l "$PACKAGE_INSTALLING_SRC_DIR"

        if [ -n "$PACKAGE_BSCRIPT" ] ; then
            step "list files of the installing build script directory"
            run ls -l "$PACKAGE_BSCRIPT_DIR"
        fi
    }

    #########################################################################################

    # https://www.gnu.org/software/gettext/manual/html_node/config_002eguess.html
    # https://git.savannah.gnu.org/cgit/config.git/tree/

    step "update config.{sub,guess}"

    {
        [ "$PACKAGE_USE_BSYSTEM_AUTOGENSH" = 1 ] ||
        [ "$PACKAGE_USE_BSYSTEM_AUTOTOOLS" = 1 ] ||
        [ "$PACKAGE_USE_BSYSTEM_CONFIGURE" = 1 ]
    } && {
        for FILENAME in config.sub config.guess
        do
            FILEPATH="$SESSION_DIR/$FILENAME"

            [ -f "$FILEPATH" ] || {
                wfetch "https://git.savannah.gnu.org/cgit/config.git/plain/$FILENAME" -o "$FILEPATH"

                run chmod a+x "$FILEPATH"

                if [ "$FILENAME" = 'config.sub' ] ; then
                    gsed -i 's/arm64-*/arm64-*|arm64e-*/g' "$FILEPATH"
                fi
            }

            find "$PACKAGE_BSCRIPT_DIR" -name "$FILENAME" -exec cp -vf "$FILEPATH" {} \;
        done
    }

    #########################################################################################

    if [ -n "$PACKAGE_ONREADY" ] ; then
        step "onready"

        if [ "$PWD" != "$PACKAGE_BSCRIPT_DIR" ] ; then
            run     cd "$PACKAGE_BSCRIPT_DIR"
        fi

        eval "
onready() {
$PACKAGE_ONREADY
}"
        onready
    fi

    #########################################################################################

    if [ -n "$PACKAGE_DO12345" ] ; then
        step "install for native"

        NATIVE_BUILD_NEEDED=1

        NATIVE_INSTALLED_VERSION_TXT_FILEPATH="$NATIVE_PACKAGE_INSTALLED_ROOT/$PACKAGE_NAME/version.txt"

        [ -f "$NATIVE_INSTALLED_VERSION_TXT_FILEPATH" ] && {
            if [ "$(cat "$NATIVE_INSTALLED_VERSION_TXT_FILEPATH")" = "$PACKAGE_VERSION" ] ; then
                NATIVE_BUILD_NEEDED=0
                note "install for native already have been done, skipped."
            else
                note "install for native already have been done, but not the same version, rebuild it."
            fi
        }

        if [ "$NATIVE_BUILD_NEEDED" = 1 ] ; then
            NATIVE_BCACHED_DIR="$PACKAGE_WORKING_DIR/src/-"
            NATIVE_INSTALL_DIR="$NATIVE_PACKAGE_INSTALLED_ROOT/$PACKAGE_INSTALL_SHA"

            cat <<EOF
NATIVE_BCACHED_DIR = $NATIVE_BCACHED_DIR
NATIVE_INSTALL_DIR = $NATIVE_INSTALL_DIR
EOF

            (
                PACKAGE_BCACHED_DIR="$NATIVE_BCACHED_DIR"
                PACKAGE_INSTALL_DIR="$NATIVE_INSTALL_DIR"

                run install -d "$PACKAGE_BCACHED_DIR"

                if [ "$PACKAGE_BINBSTD" = 1 ] ; then
                    run cd "$PACKAGE_BSCRIPT_DIR"
                else
                    run cd "$PACKAGE_BCACHED_DIR"
                fi

                [ "$DUMP_ENV" = 1 ] && {
                    run export -p
                    echo
                }

                eval "
build_for_native() {
$PACKAGE_DO12345
}"

                BUILD_FOR_NATIVE=1

                build_for_native
            )

            [ -d "$NATIVE_INSTALL_DIR" ] && {
                printf '%s\n' "$PACKAGE_VERSION" > "$NATIVE_INSTALL_DIR/version.txt"
                run ln -s -r -f -T "$NATIVE_INSTALL_DIR" "$NATIVE_PACKAGE_INSTALLED_ROOT/$PACKAGE_NAME"
            }
        else
            NATIVE_INSTALL_DIR="$NATIVE_PACKAGE_INSTALLED_ROOT/$PACKAGE_NAME"
        fi

        [ -d "$NATIVE_INSTALL_DIR" ] && {
            if [ -d  "$NATIVE_INSTALL_DIR/bin" ] ; then
                PATH="$NATIVE_INSTALL_DIR/bin:$PATH"
            fi

            if [ -d  "$NATIVE_INSTALL_DIR/sbin" ] ; then
                PATH="$NATIVE_INSTALL_DIR/sbin:$PATH"
            fi

            if [ -d          "$NATIVE_INSTALL_DIR/share/aclocal" ] ; then
                ACLOCAL_PATH="$NATIVE_INSTALL_DIR/share/aclocal:$ACLOCAL_PATH"
            fi
        }
    fi

    #########################################################################################
    #                            below is for target                                        #
    #########################################################################################

    if [ "$TARGET_PLATFORM_ARCH" = armv7a ] ; then
        TARGET_TRIPLE='armv7a-linux-androideabi'
    else
        TARGET_TRIPLE="$TARGET_PLATFORM_ARCH-linux-android"
    fi

    export ANDROID_NDK_COMPILER_ARGS="--target=${TARGET_TRIPLE}${TARGET_PLATFORM_VERS} --sysroot=$ANDROID_NDK_SYSROOT"

    export ANDROID_NDK_CC
    export ANDROID_NDK_CXX

    #########################################################################################

    export      CC="$NDKPKG_CORE_DIR/wrapper-target-cc"
    export     CXX="$NDKPKG_CORE_DIR/wrapper-target-c++"
    export     CPP="$CC -E"
    export      LD="$ANDROID_NDK_LD"
    export      AS="$ANDROID_NDK_AS"
    export      AR="$ANDROID_NDK_AR"
    export      NM="$ANDROID_NDK_NM"
    export    SIZE="$ANDROID_NDK_SIZE"
    export   STRIP="$ANDROID_NDK_STRIP"
    export  RANLIB="$ANDROID_NDK_RANLIB"
    export STRINGS="$ANDROID_NDK_STRINGS"
    export OBJDUMP="$ANDROID_NDK_OBJDUMP"
    export OBJCOPY="$ANDROID_NDK_OBJCOPY"
    export READELF="$ANDROID_NDK_READELF"
    export SYSROOT="$ANDROID_NDK_SYSROOT"

    #########################################################################################

    NDKPKG_COMMON_H_FILEPATH="$PACKAGE_INSTALLING_INC_DIR/ndk-pkg-common.h"

    #########################################################################################

    # https://gcc.gnu.org/onlinedocs/gcc-4.3.6/gcc/Warning-Options.html
    CCFLAGS="-Qunused-arguments -fPIC -Wall"
    XXFLAGS="-Qunused-arguments -fPIC -Wall"
    PPFLAGS="-Qunused-arguments -include $NDKPKG_COMMON_H_FILEPATH"
    LDFLAGS="-Wl,--as-needed -Wl,-z,muldefs -Wl,--allow-multiple-definition"

    CCFLAGS="$CCFLAGS $PACKAGE_CCFLAGS"
    XXFLAGS="$XXFLAGS $PACKAGE_XXFLAGS"
    PPFLAGS="$PPFLAGS $PACKAGE_PPFLAGS"
    LDFLAGS="$LDFLAGS $PACKAGE_LDFLAGS"

    ##############################################

    _F_COMMON_OPT_IS_SET=0

    for item in $PACKAGE_CCFLAGS
    do
        [ "$item" = '-fcommon' ] && {
            _F_COMMON_OPT_IS_SET=1
            break
        }
    done

    if [ "$_F_COMMON_OPT_IS_SET" = 0 ] ; then
        CCFLAGS="$CCFLAGS -fno-common"
    fi

    ##############################################

    _F_COMMON_OPT_IS_SET=0

    for item in $PACKAGE_XXFLAGS
    do
        [ "$item" = '-fcommon' ] && {
            _F_COMMON_OPT_IS_SET=1
            break
        }
    done

    if [ "$_F_COMMON_OPT_IS_SET" = 0 ] ; then
        XXFLAGS="$XXFLAGS -fno-common"
    fi

    ##############################################

    CCFLAGS="$(printf '%s\n' "$CCFLAGS" | sed -e 's|--static||g' -e 's|-static||g')"
    XXFLAGS="$(printf '%s\n' "$XXFLAGS" | sed -e 's|--static||g' -e 's|-static||g')"
    LDFLAGS="$(printf '%s\n' "$LDFLAGS" | sed -e 's|--static||g' -e 's|-static||g')"

    ##############################################

    if [ "$PACKAGE_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE" = 1 ] ; then
        LDFLAGS="-static --static -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--no-dynamic-linker $LDFLAGS"
    fi

    ##############################################

    if [ "$DEBUG_CC" = 1 ] ; then
        CCFLAGS="$CCFLAGS -v"
        XXFLAGS="$XXFLAGS -v"
    fi

    if [ "$DEBUG_LD" = 1 ] ; then
        LDFLAGS="$LDFLAGS -Wl,-v"
    fi

    ##############################################

    case $PROFILE in
        debug)
            CCFLAGS="$CCFLAGS -O0 -g"
            XXFLAGS="$XXFLAGS -O0 -g"
            ;;
        release)
            CCFLAGS="$CCFLAGS -Os"
            XXFLAGS="$XXFLAGS -Os"

            unset _U_NDEBUG_OPT_IS_SET

            for item in $PACKAGE_PPFLAGS
            do
                [ "$item" = '-UNDEBUG' ] && {
                    _U_NDEBUG_OPT_IS_SET=1
                    break
                }
            done

            if [ "$_U_NDEBUG_OPT_IS_SET" != 1 ] ; then
                PPFLAGS="$PPFLAGS -DNDEBUG"
            fi

            if [ -z "$ENABLE_LTO" ] || [ "$ENABLE_LTO" = 1 ] ; then
                LDFLAGS="$LDFLAGS -flto"
            fi

            case $ENABLE_STRIP in
                all)   LDFLAGS="$LDFLAGS -Wl,-s" ;;
                debug) LDFLAGS="$LDFLAGS -Wl,-S" ;;
            esac
    esac

    ##############################################

    for DEPENDENT_PACKAGE_NAME in $RECURSIVE_DEPENDENT_PACKAGE_NAMES
    do
        DEPENDENT_PACKAGE_NAME_UNDERSCORE=$(printf '%s\n' "$DEPENDENT_PACKAGE_NAME" | tr '@+-.' '_')

        DEPENDENT_PACKAGE_INSTALL_DIR="$NDKPKG_PACKAGE_INSTALLED_ROOT/$TARGET_PLATFORM_SPEC/$DEPENDENT_PACKAGE_NAME"
        DEPENDENT_PACKAGE_INCLUDE_DIR="$DEPENDENT_PACKAGE_INSTALL_DIR/include"
        DEPENDENT_PACKAGE_LIBRARY_DIR="$DEPENDENT_PACKAGE_INSTALL_DIR/lib"
        DEPENDENT_PACKAGE_PKGCONF_DIR="$DEPENDENT_PACKAGE_INSTALL_DIR/lib/pkgconfig"

        eval "${DEPENDENT_PACKAGE_NAME_UNDERSCORE}_INSTALL_DIR='$DEPENDENT_PACKAGE_INSTALL_DIR'"
        eval "${DEPENDENT_PACKAGE_NAME_UNDERSCORE}_INCLUDE_DIR='$DEPENDENT_PACKAGE_INCLUDE_DIR'"
        eval "${DEPENDENT_PACKAGE_NAME_UNDERSCORE}_LIBRARY_DIR='$DEPENDENT_PACKAGE_LIBRARY_DIR'"

        if [ -d "$DEPENDENT_PACKAGE_INCLUDE_DIR" ] ; then
            PPFLAGS="-I$DEPENDENT_PACKAGE_INCLUDE_DIR $PPFLAGS"
        fi

        if [ -d "$DEPENDENT_PACKAGE_LIBRARY_DIR" ] ; then
            LDFLAGS="-L$DEPENDENT_PACKAGE_LIBRARY_DIR -Wl,-rpath-link,$DEPENDENT_PACKAGE_LIBRARY_DIR $LDFLAGS"
        fi
    done

    PPFLAGS="-I$PACKAGE_INSTALLING_INC_DIR $PPFLAGS"
    LDFLAGS="-L$PACKAGE_INSTALLING_LIB_DIR $LDFLAGS"

    for DEPENDENT_PACKAGE_NAME in $PACKAGE_DEP_PKG
    do
        # https://android.googlesource.com/platform/bionic/+/master/docs/status.md

        case $DEPENDENT_PACKAGE_NAME in
            libexecinfo)
                if [ "$TARGET_PLATFORM_VERS" -lt 33 ] ; then
                    # https://android.googlesource.com/platform/bionic/+/master/libc/include/execinfo.h
                    # https://android.googlesource.com/platform/bionic/+/master/libc/bionic/execinfo.cpp
                    PPFLAGS="$PPFLAGS -include $libexecinfo_INCLUDE_DIR/execinfo.h"
                    LDFLAGS="$LDFLAGS -l:libexecinfo.a -lm"
                fi
                ;;
            libgetloadavg)
                if [ "$TARGET_PLATFORM_VERS" -lt 29 ] ; then
                    # https://android.googlesource.com/platform/bionic/+/master/libc/include/stdlib.h#157
                    # https://android.googlesource.com/platform/bionic/+/master/libc/bionic/getloadavg.cpp
                    # int getloadavg(double __averages[], int __n) __INTRODUCED_IN(29);
                    PPFLAGS="$PPFLAGS -include $libgetloadavg_INCLUDE_DIR/getloadavg.h"
                    LDFLAGS="$LDFLAGS -l:libgetloadavg.a"
                fi
                ;;
            libglob)
                # https://pubs.opengroup.org/onlinepubs/9699919799/functions/glob.html
                # https://android.googlesource.com/platform/bionic/+/master/libc/include/glob.h
                if [ "$TARGET_PLATFORM_VERS" -lt 28 ] ; then
                    LDFLAGS="$LDFLAGS -l:libglob.a"
                fi
                ;;
            liblanginfo)
                # https://pubs.opengroup.org/onlinepubs/9699919799/functions/nl_langinfo.html
                # https://android.googlesource.com/platform/bionic/+/master/libc/include/langinfo.h
                if [ "$TARGET_PLATFORM_VERS" -lt 26 ] ; then
                    LDFLAGS="$LDFLAGS -l:liblanginfo.a"
                fi
                ;;
            libmblen)
                # https://android.googlesource.com/platform/bionic/+/master/libc/include/stdlib.h#163
                # https://android.googlesource.com/platform/bionic/+/master/libc/bionic/mblen.cpp
                # int mblen(const char* __s, size_t __n) __INTRODUCED_IN_NO_GUARD_FOR_NDK(26);
                if [ "$TARGET_PLATFORM_VERS" -lt 26 ] ; then
                    PPFLAGS="$PPFLAGS -include $libmblen_INCLUDE_DIR/mblen.h"
                    LDFLAGS="$LDFLAGS -l:libmblen.a"
                fi
                ;;
            libgetdomainname)
                # https://android.googlesource.com/platform/bionic/+/master/libc/include/unistd.h#313
                # https://android.googlesource.com/platform/bionic/+/master/libc/bionic/getdomainname.cpp
                # int getdomainname(char* __buf, size_t __buf_size) __INTRODUCED_IN(26);
                if [ "$TARGET_PLATFORM_VERS" -lt 26 ] ; then
                    PPFLAGS="$PPFLAGS -include $libgetdomainname_INCLUDE_DIR/getdomainname.h"
                    LDFLAGS="$LDFLAGS -l:libgetdomainname.a"
                fi
                ;;
            libstrchrnul)
                # https://android.googlesource.com/platform/bionic/+/master/libc/include/string.h#68
                # char* strchrnul(char* __s, int __ch) __RENAME(strchrnul) __attribute_pure__ __INTRODUCED_IN(24);
                if [ "$TARGET_PLATFORM_VERS" -lt 24 ] ; then
                    PPFLAGS="$PPFLAGS -include $libstrchrnul_INCLUDE_DIR/strchrnul.h"
                    LDFLAGS="$LDFLAGS -l:libstrchrnul.a"
                fi
                ;;
            libgetdtablesize)
                # https://android.googlesource.com/platform/bionic/+/72dc1c22dc6a92dea925398c9e3880364ab29c1c/libc/bionic/getdtablesize.c
                PPFLAGS="$PPFLAGS -include $libgetdtablesize_INCLUDE_DIR/getdtablesize.h"
                LDFLAGS="$LDFLAGS -l:libgetdtablesize.a"
                ;;
        esac
    done

    export   CFLAGS="$CCFLAGS"
    export CXXFLAGS="$XXFLAGS"
    export CPPFLAGS="$PPFLAGS"
    export  LDFLAGS="$LDFLAGS"

    #########################################################################################

    # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
    export XDG_DATA_DIRS

    for DEPENDENT_PACKAGE_INSTALL_DIR in $RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS
    do
        # https://gi.readthedocs.io/en/latest/tools/g-ir-scanner.html#environment-variables
        if [ -d "$DEPENDENT_PACKAGE_INSTALL_DIR/share/gir-1.0" ] ; then
            if [ -z "$XDG_DATA_DIRS" ] ; then
                XDG_DATA_DIRS="$DEPENDENT_PACKAGE_INSTALL_DIR/share"
            else
                XDG_DATA_DIRS="$DEPENDENT_PACKAGE_INSTALL_DIR/share:$XDG_DATA_DIRS"
            fi
        fi

        # https://help.gnome.org/admin//system-admin-guide/2.32/mimetypes-database.html.en
        if [ -d "$DEPENDENT_PACKAGE_INSTALL_DIR/share/mime" ] ; then
            if [ -z "$XDG_DATA_DIRS" ] ; then
                XDG_DATA_DIRS="$DEPENDENT_PACKAGE_INSTALL_DIR/share"
            else
                XDG_DATA_DIRS="$DEPENDENT_PACKAGE_INSTALL_DIR/share:$XDG_DATA_DIRS"
            fi
        fi
    done

    #########################################################################################

    # https://www.gnu.org/software/automake/manual/html_node/Macro-Search-Path.html
    export ACLOCAL_PATH

    for DEPENDENT_PACKAGE_INSTALL_DIR in $RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS
    do
        DEPENDENT_PACKAGE_ACLOCAL_PATH="$DEPENDENT_PACKAGE_INSTALL_DIR/share/aclocal"

        if [ -d "$DEPENDENT_PACKAGE_ACLOCAL_PATH" ] ; then
            if [ -z "$ACLOCAL_PATH" ] ; then
                ACLOCAL_PATH="$DEPENDENT_PACKAGE_ACLOCAL_PATH"
            else
                ACLOCAL_PATH="$DEPENDENT_PACKAGE_ACLOCAL_PATH:$ACLOCAL_PATH"
            fi
        fi
    done

    #########################################################################################

    PATH="$PACKAGE_INSTALLING_BIN_DIR:$ANDROID_NDK_HOME:$PATH"

    #########################################################################################

    # override the default search directory (usually /usr/lib/pkgconfig:/usr/share/pkgconfig)
    # because we only want to use our own
    export PKG_CONFIG_LIBDIR="$PACKAGE_WORKING_DIR/lib/pkgconfig"
    export PKG_CONFIG_PATH="$PACKAGE_WORKING_DIR/lib/pkgconfig"

    for DEPENDENT_PACKAGE_INSTALL_DIR in $RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS
    do
        for d in lib share
        do
            DEPENDENT_PACKAGE_PKGCONF_DIR="$DEPENDENT_PACKAGE_INSTALL_DIR/$d/pkgconfig"

            if [ -d "$DEPENDENT_PACKAGE_PKGCONF_DIR" ] ; then
                PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$DEPENDENT_PACKAGE_PKGCONF_DIR"
            fi
        done
    done

    #########################################################################################

    for item in $PACKAGE_DEP_LIB
    do
        case $item in
            -l*);;
            *)  item="$(pkg-config --libs-only-l "$item")"
        esac

        ANDROID_NDK_COMPILER_ARGS="$ANDROID_NDK_COMPILER_ARGS $item"
    done

    #########################################################################################

    if [ "$PACKAGE_USE_BSYSTEM_CMAKE" = 1 ] ; then
        # https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html#manual:cmake-env-variables(7)

        unset CMAKE_PREFIX_PATH
        unset CMAKE_APPLE_SILICON_PROCESSOR
        unset CMAKE_BUILD_PARALLEL_LEVEL
        unset CMAKE_BUILD_TYPE
        unset CMAKE_CONFIGURATION_TYPES
        unset CMAKE_CONFIG_TYPE
        unset CMAKE_EXPORT_COMPILE_COMMANDS
        unset CMAKE_GENERATOR
        unset CMAKE_GENERATOR_INSTANCE
        unset CMAKE_GENERATOR_PLATFORM
        unset CMAKE_GENERATOR_TOOLSET
        unset CMAKE_INSTALL_MODE
        unset CMAKE_C_COMPILER_LAUNCHER
        unset CMAKE_C_LINKER_LAUNCHER
        unset CMAKE_CXX_COMPILER_LAUNCHER
        unset CMAKE_CXX_LINKER_LAUNCHER
        unset CMAKE_MSVCIDE_RUN_PATH
        unset CMAKE_NO_VERBOSE
        unset CMAKE_OSX_ARCHITECTURES
        unset CMAKE_TOOLCHAIN_FILE
        unset DESTDIR
        unset CTEST_INTERACTIVE_DEBUG_MODE
        unset CTEST_OUTPUT_ON_FAILURE
        unset CTEST_PARALLEL_LEVEL
        unset CTEST_PROGRESS_OUTPUT
        unset CTEST_USE_LAUNCHERS_DEFAULT
        unset DASHBOARD_TEST_FROM_CTEST

        # https://cmake.org/cmake/help/latest/envvar/CMAKE_BUILD_PARALLEL_LEVEL.html
        export CMAKE_BUILD_PARALLEL_LEVEL="$BUILD_NJOBS"

        # https://cmake.org/cmake/help/latest/envvar/CMAKE_GENERATOR.html
        if [ "$PACKAGE_USE_BSYSTEM_NINJA" = 1 ] ; then
            export CMAKE_GENERATOR='Ninja'
        else
            export CMAKE_GENERATOR='Unix Makefiles'
        fi

        # https://cmake.org/cmake/help/latest/envvar/CMAKE_EXPORT_COMPILE_COMMANDS.html
        if [ "$REQUEST_TO_EXPORT_COMPILE_COMMANDS_JSON" = 1 ] ; then
            export CMAKE_EXPORT_COMPILE_COMMANDS=ON
        else
            export CMAKE_EXPORT_COMPILE_COMMANDS=OFF
        fi

        case $PROFILE in
            debug)   CMAKE_BUILD_TYPE=Debug   ;;
            release) CMAKE_BUILD_TYPE=Release ;;
        esac

        if [ "$VERBOSE_CMAKE" = 1 ] ; then
            CMAKE_VERBOSE_MAKEFILE=ON
            CMAKE_COLOR_MAKEFILE=ON
            CMAKE_INSTALL_MESSAGE=ALWAYS
        else
            CMAKE_VERBOSE_MAKEFILE=OFF
            CMAKE_COLOR_MAKEFILE=OFF
            CMAKE_INSTALL_MESSAGE=NEVER
        fi

        # https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_DEBUG_MODE.html
        if [ "$DEBUG_CMAKE" = 1 ] ; then
            CMAKE_FIND_DEBUG_MODE=ON
        else
            CMAKE_FIND_DEBUG_MODE=OFF
        fi

        # https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_ROOT_PATH.html
        unset CMAKE_FIND_ROOT_PATH

        if [ -n "$PACKAGE_DEP_PKG" ] ; then
            CMAKE_FIND_ROOT_PATH="$(printf '%s\n' "$RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS" | tr ' ' ';')"
        fi
    fi

    #########################################################################################

    if [ "$PACKAGE_USE_BSYSTEM_CARGO" = 1 ] ; then
        # https://docs.rs/backtrace/latest/backtrace/
        export RUST_BACKTRACE=1

        # this environment variable is not defined by Rust, but it is widely used by third-party project.
        case $TARGET_PLATFORM_ARCH in
            armv7a)  export RUST_TARGET='armv7-linux-androideabi' ;;
            aarch64) export RUST_TARGET='aarch64-linux-android'   ;;
            i686)    export RUST_TARGET='i686-linux-android'      ;;
            x86_64)  export RUST_TARGET='x86_64-linux-android'    ;;
        esac

        RUST_TARGET_UPPERCASE_UNDERSCORE=$(printf '%s\n' "$RUST_TARGET" | tr a-z A-Z | tr - _)

        # https://doc.rust-lang.org/cargo/reference/config.html#environment-variables
        # https://doc.rust-lang.org/cargo/reference/environment-variables.html
        export "CARGO_TARGET_${RUST_TARGET_UPPERCASE_UNDERSCORE}_AR"="$AR"
        export "CARGO_TARGET_${RUST_TARGET_UPPERCASE_UNDERSCORE}_LINKER"="$CC"

        export CARGO_BUILD_JOBS="$BUILD_NJOBS"

        #########################################################################

        # https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg
        export RUSTFLAGS="-Clinker=$CC"

        for LDFLAG in $LDFLAGS
        do
            RUSTFLAGS="$RUSTFLAGS -Clink-arg=$LDFLAG"
        done

        #if [ "$TARGET_PLATFORM_ARCH" = x86_64 ] || [ "$TARGET_PLATFORM_ARCH" = i686 ] ; then
            LIBCLANG_RT_BUILTINS_FILEPATH="$("$CC" -print-libgcc-file-name)"
            RUSTFLAGS="$RUSTFLAGS -Clink-arg=$LIBCLANG_RT_BUILTINS_FILEPATH"
        #fi

        #########################################################################

        RUST_TARGET_FOR_BUILD="$(rustc -vV | sed -n '/host: /p' | cut -c7- | tr 'a-z-' 'A-Z_')"

        # https://doc.rust-lang.org/cargo/reference/config.html#environment-variables
        # https://doc.rust-lang.org/cargo/reference/environment-variables.html
        export "CARGO_TARGET_${RUST_TARGET_FOR_BUILD}_AR"="$AR_FOR_BUILD"
        export "CARGO_TARGET_${RUST_TARGET_FOR_BUILD}_LINKER"="$CC_FOR_BUILD"

        #########################################################################

        # https://docs.rs/openssl/latest/openssl/
        [ "$PACKAGE_DEP_UPP_LIBOPENSSL" = 1 ] && {
            eval export "${RUST_TARGET_FOR_BUILD}_OPENSSL_DIR='$NATIVE_PACKAGE_INSTALLED_ROOT/openssl'"
        }

        #########################################################################

        for DEPENDENT_PACKAGE_NAME in $RECURSIVE_DEPENDENT_PACKAGE_NAMES
        do
            case $DEPENDENT_PACKAGE_NAME in
                openssl@1.1)
                    # https://docs.rs/openssl/latest/openssl/
                    export OPENSSL_DIR="$openssl_1_1_INSTALL_DIR"

                    if [ "$PACKAGE_PKGTYPE" = exe ] ; then
                        # https://github.com/sfackler/rust-openssl/blob/master/openssl-sys/build/main.rs
                        export OPENSSL_STATIC=1
                        export OPENSSL_NO_VENDOR=1
                    fi
                    ;;
                openssl-dev)
                    # https://docs.rs/openssl/latest/openssl/
                    export OPENSSL_DIR="$openssl_dev_INSTALL_DIR"

                    if [ "$PACKAGE_PKGTYPE" = exe ] ; then
                        # https://github.com/sfackler/rust-openssl/blob/master/openssl-sys/build/main.rs
                        export OPENSSL_STATIC=1
                        export OPENSSL_NO_VENDOR=1
                    fi
                    ;;
                libssh2)
                    if [ "$PACKAGE_PKGTYPE" = exe ] ; then
                        # https://github.com/alexcrichton/ssh2-rs/blob/master/libssh2-sys/build.rs
                        export LIBSSH2_SYS_USE_PKG_CONFIG=1
                    fi
                    ;;
                libgit2)
                    if [ "$PACKAGE_PKGTYPE" = exe ] ; then
                        # https://github.com/rust-lang/git2-rs/blob/master/libgit2-sys/build.rs
                        export LIBGIT2_NO_VENDOR=1
                        export LIBGIT2_SYS_USE_PKG_CONFIG=1
                    fi
                    ;;
                libz|zlib)
                    if [ "$PACKAGE_PKGTYPE" = exe ] ; then
                        # https://github.com/rust-lang/libz-sys/blob/main/build.rs
                        export LIBZ_SYS_STATIC=1
                    fi
                    ;;
            esac
        done

        #########################################################################

        # https://libraries.io/cargo/cc
        # https://crates.io/crates/cc
        # https://docs.rs/cc/latest/cc/
        # https://github.com/alexcrichton/cc-rs
        export HOST_CC="$CC_FOR_BUILD"
        export HOST_CFLAGS="$CFLAGS_FOR_BUILD"

        export HOST_CXX="$CXX_FOR_BUILD"
        export HOST_CXXFLAGS="$CXXFLAGS_FOR_BUILD"

        export HOST_AR="$AR_FOR_BUILD"

        export TARGET_CC="$CC"
        export TARGET_CFLAGS="$CFLAGS $CPPFLAGS $LDFLAGS"

        export TARGET_CXX="$CXX"
        export TARGET_CXXFLAGS="$CXXFLAGS $CPPFLAGS $LDFLAGS"

        export TARGET_AR="$AR"

        #########################################################################

        # https://libraries.io/cargo/pkg-config
        # https://crates.io/crates/pkg-config
        # https://docs.rs/pkg-config/latest/pkg_config/
        # https://github.com/rust-lang/pkg-config-rs
        export TARGET_PKG_CONFIG_ALLOW_CROSS=1

        export HOST_PKG_CONFIG_PATH="$PKG_CONFIG_PATH_FOR_BUILD"
        export HOST_PKG_CONFIG_LIBDIR="$NDKPKG_CORE_DIR/lib"

        unset PKG_CONFIG_SYSROOT_DIR

        #########################################################################

        # https://libraries.io/cargo/cmake
        # https://crates.io/crates/cmake
        # https://docs.rs/cmake/latest/cmake/
        # https://github.com/alexcrichton/cmake-rs
        # this variable is not motioned in their document. you must read the source code of cmake-rs crate.
        export TARGET_CMAKE_TOOLCHAIN_FILE="$PACKAGE_WORKING_DIR/android.toolchain.cmake"

        cat > "$TARGET_CMAKE_TOOLCHAIN_FILE" <<EOF
set(ANDROID_ABI "${TARGET_PLATFORM_ABI}")
set(ANDROID_PLATFORM "android-${TARGET_PLATFORM_VERS}")
include(${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake)

set(CMAKE_C_COMPILER "$CC")
set(CMAKE_C_FLAGS "$CPPFLAGS $CFLAGS")

set(CMAKE_CXX_COMPILER "$CXX")
set(CMAKE_CXX_FLAGS "$CPPFLAGS $CXXFLAGS")

set(CMAKE_ASM_COMPILER "$CC")

set(CMAKE_SHARED_LINKER_FLAGS "$(printf '%s\n' "$LDFLAGS" | sed -e 's|--static||g' -e 's|-static||g')")
set(CMAKE_EXE_LINKER_FLAGS    "$LDFLAGS")

set(CMAKE_C_COMPILER_AR     "$AR")
set(CMAKE_C_COMPILER_RANLIB "$RANLIB")

set(CMAKE_CXX_COMPILER_AR     "$AR")
set(CMAKE_CXX_COMPILER_RANLIB "$RANLIB")

set(CMAKE_AR      "$AR")
set(CMAKE_RANLIB  "$RANLIB")

set(CMAKE_LINKER  "$LD")

set(CMAKE_NM      "$NM")
set(CMAKE_READELF "$READELF")

set(CMAKE_OBJCOPY "$OBJCOPY")
set(CMAKE_OBJDUMP "$OBJDUMP")

set(CMAKE_STRIP   "$STRIP")

set(CMAKE_ADDR2LINE "$ADDR2LINE")
EOF
    fi

    #########################################################################################

    if [ "$PACKAGE_USE_BSYSTEM_GO" = 1 ] ; then
        # https://pkg.go.dev/cmd/cgo
        export CGO_ENABLED=1
        export CGO_CFLAGS="$CFLAGS"
        export CGO_CXXFLAGS="$CXXFLAGS"
        export CGO_CPPFLAGS="$CPPFLAGS"
        export CGO_LDFLAGS="$LDFLAGS"

        # https://go.dev/blog/go116-module-changes
        export GO111MODULE='auto'

        if [ "$COUNTRY" = china ] ; then
            export GOPROXY='https://goproxy.cn'
        fi

        # https://golang.org/doc/install/source#environment
        export GOOS='android'

        case $TARGET_PLATFORM_ARCH in
            armv7a)  export GOARCH=arm   ;;
            aarch64) export GOARCH=arm64 ;;
            i686)    export GOARCH=386   ;;
            x86_64)  export GOARCH=amd64 ;;
        esac
    fi

    #########################################################################################

    # https://pubs.opengroup.org/onlinepubs/000095399/functions/rindex.html
    # https://pubs.opengroup.org/onlinepubs/000095399/functions/bcmp.html
    # https://linux.die.net/man/2/wait3
    # https://stackoverflow.com/questions/32826175/ftello-and-fseeko-android-build-errors
    # https://linux.die.net/man/3/ftello
    # int   fseeko(FILE* __fp, off_t __offset, int __whence) __RENAME(fseeko64) __INTRODUCED_IN(24);
    # off_t ftello(FILE* __fp) __RENAME(ftello64) __INTRODUCED_IN(24);

    cat > "$NDKPKG_COMMON_H_FILEPATH" <<EOF
#ifndef NDKPKG_COMMON_H
#define NDKPKG_COMMON_H

//#define rindex(a,b) strrchr((a),(b))

#define bcmp(b1,b2,len) memcmp((b1), (b2), (size_t)(len))

#define wait3(status,options,rusage) waitpid(-1,status,options)

#if __ANDROID_API__ < 24
    #define ftello(f) ftell(f)
    #define fseeko    fseek
#endif

#endif
EOF

    #########################################################################################

    # https://developer.android.com/ndk/guides/stable_apis#c_library
    # Note that on Android, unlike Linux, there are no separate libpthread or librt libraries.
    # That functionality is included directly in libc, which does not need to be explicitly linked against.
    printf '!<arch>\n' > "$PACKAGE_WORKING_DIR/lib/libpthread.a"
    printf '!<arch>\n' > "$PACKAGE_WORKING_DIR/lib/librt.a"

    if [ "$PACKAGE_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE" = 1 ] ; then
        printf '!<arch>\n' > "$PACKAGE_WORKING_DIR/lib/liblog.a"
    fi

    if [ "$ANDROID_NDK_VERSION_MAJOR" -ge 23 ] ; then
        # https://github.com/rust-windowing/android-ndk-rs/issues/149
        if [ "$TARGET_PLATFORM_ARCH" = 'x86_64' ] ; then
            LIBCLANG_RT_BUILTINS_FILEPATH="$("$CC" -print-libgcc-file-name)"
        else
            LIBCLANG_RT_BUILTINS_FILEPATH=
        fi

        printf 'INPUT(%s -l:libunwind.a)\n' "$LIBCLANG_RT_BUILTINS_FILEPATH" > "$PACKAGE_WORKING_DIR/lib/libgcc.a"
    fi

    #########################################################################################

    [ -n "$RECURSIVE_DEPENDENT_PACKAGE_NAMES" ] && {
        if [ "$PACKAGE_PKGTYPE" = exe ] || [ "$PACKAGE_PKGTYPE" = pie ] ; then
            step "copy dependent libraries to linker first search dir"

            for DEPENDENT_PACKAGE_INSTALL_DIR in $RECURSIVE_DEPENDENT_PACKAGE_INSTALL_DIRS
            do
                DEPENDENT_PACKAGE_LIBRARY_DIR="$DEPENDENT_PACKAGE_INSTALL_DIR/lib"

                if [  -d "$DEPENDENT_PACKAGE_LIBRARY_DIR" ] ; then
                    find "$DEPENDENT_PACKAGE_LIBRARY_DIR" -maxdepth 1 -mindepth 1 -name 'lib*.a' -exec cp -L -v '{}' "$PACKAGE_WORKING_DIR/lib/" \;
                fi
            done
        fi
    }

    #########################################################################################

    [ "$ENABLE_CCACHE" = 1 ] && {
        step "setup ccache"

        run ln -sf "$CCACHE" "$PACKAGE_WORKING_DIR/bin/wrapper-target-cc"
        run ln -sf "$CCACHE" "$PACKAGE_WORKING_DIR/bin/wrapper-target-c++"

        ccache -s > "$PACKAGE_WORKING_DIR/ccache-s.txt"
    }

    #########################################################################################

    step "dopatch for target"

    [ -f "$PACKAGE_INSTALLING_FIX_DIR/index" ] && {
        cd "$PACKAGE_BSCRIPT_DIR"

        for LINE in $(cat "$PACKAGE_INSTALLING_FIX_DIR/index")
        do
            FILE="$(printf '%s\n' "$LINE" | cut -d '|' -f1)"
            OPTS="$(printf '%s\n' "$LINE" | cut -d '|' -f2)"
            [ -z "$OPTS" ] && OPTS='-p1'
            run "patch $OPTS < $PACKAGE_INSTALLING_FIX_DIR/$FILE"
        done
    }

    [ -n "$PACKAGE_DOPATCH" ] && {
        cd "$PACKAGE_BSCRIPT_DIR"

        eval "
dopatch() {
$PACKAGE_DOPATCH
}"
        dopatch
    }

    #########################################################################################

    cd "$PACKAGE_BSCRIPT_DIR"

    #########################################################################################

    # https://github.com/golang/go/issues/65568
    [ -f go.mod ] && gsed -i 's|^go 1.22$|go 1.22.0|' go.mod

    #########################################################################################

    case $PACKAGE_BSYSTEM_MASTER in
        autogen)
            if [ -f configure ] ; then
                CONFIGURE_FILE_LAST_MODIFIED_TIMESTAMP="$(stat --format=%Y configure)"

                if [ -z "$CONFIGURE_FILE_LAST_MODIFIED_TIMESTAMP" ] ; then
                    run NOCONFIGURE=yes ./autogen.sh
                elif [ "$CONFIGURE_FILE_LAST_MODIFIED_TIMESTAMP" -lt "$TIMESTAMP_UNIX" ] ; then
                    run NOCONFIGURE=yes ./autogen.sh
                fi
            else
                run NOCONFIGURE=yes ./autogen.sh
            fi
            ;;
        autotools)
            if [ -f configure ] ; then
                CONFIGURE_FILE_LAST_MODIFIED_TIMESTAMP="$(stat --format=%Y configure)"

                if [ -z "$CONFIGURE_FILE_LAST_MODIFIED_TIMESTAMP" ] ; then
                    run autoreconf -ivf
                elif [ "$CONFIGURE_FILE_LAST_MODIFIED_TIMESTAMP" -lt "$TIMESTAMP_UNIX" ] ; then
                    run autoreconf -ivf
                fi
            else
                run autoreconf -ivf
            fi
            ;;
    esac

    #########################################################################################

    [ -n "$PACKAGE_PREPARE" ] && {
        step "prepare for target"

        eval "
prepare() {
$PACKAGE_PREPARE
}"
        prepare
    }

    #########################################################################################

    step "install for target"

    if [ "$PACKAGE_BINBSTD" = 1 ] ; then
        run cd "$PACKAGE_BSCRIPT_DIR"
    else
        run cd "$PACKAGE_BCACHED_DIR"
    fi

    if [        -d "$PACKAGE_INSTALL_DIR" ] ; then
        run rm -rf "$PACKAGE_INSTALL_DIR"
    fi

    [ "$DUMP_ENV" = 1 ] && {
        run export -p
        echo
    }

    eval "
dobuild() {
$PACKAGE_DOBUILD
}"

    dobuild

    #########################################################################################

    [   -d "$PACKAGE_INSTALL_DIR" ] || abort 1 "nothing was installed."

    step "change to the installed directory"
    run cd "$PACKAGE_INSTALL_DIR"

    [ -z "$(ls)" ]                  && abort 1 "nothing was installed."

    #########################################################################################

    step "dotweak for target"

    __tweak_pc_files

    [ -n "$PACKAGE_DOTWEAK" ] && {
        eval "
dotweak() {
$PACKAGE_DOTWEAK
}"
        dotweak
    }

    #########################################################################################

    [ -d  "$PACKAGE_INSTALL_DIR/lib" ] && {
        # https://www.linuxfromscratch.org/blfs/view/stable-systemd/introduction/la-files.html
        # remove Libtool Archive (.la) files
        find "$PACKAGE_INSTALL_DIR/lib" -maxdepth 1 -mindepth 1 \( -type f -or -type l \) -name '*.la' -exec rm '{}' \;
    }

    #########################################################################################

    PACKAGE_METAINFO_DIR="$PACKAGE_INSTALL_DIR/.ndk-pkg"

    PACKAGE_MANIFEST_FILEPATH="$PACKAGE_METAINFO_DIR/MANIFEST.txt"
    PACKAGE_RECEIPT_FILEPATH="$PACKAGE_METAINFO_DIR/RECEIPT.yml"

    PACKAGE_DEPENDENT_SHARED_LIBS_DIR="$PACKAGE_METAINFO_DIR/dependencies/lib"

    install -d "$PACKAGE_METAINFO_DIR"

    #########################################################################################

    # executable that need to be set rpath : $ORIGIN/../lib
    unset PACKAGE_EXECUTABLES_NEED_SET_RPATH_S1

    # executable that need to be set rpath : $ORIGIN/../.ndk-pkg/dependencies/lib
    unset PACKAGE_EXECUTABLES_NEED_SET_RPATH_S2

    step "docheck for target"
    __check_elf_files

    #########################################################################################

    [ "$PACKAGE_USE_BSYSTEM_CARGO" = 1 ] && {
        rm -f "$PACKAGE_INSTALL_DIR/.crates.toml"
        rm -f "$PACKAGE_INSTALL_DIR/.crates2.json"
    }

    #########################################################################################

    for item in 'FAQ*' 'TODO*' 'NEWS*' 'THANKS*' 'README*' 'COPYING*' 'LICENSE*' 'AUTHORS*' 'CHANGES*' 'CHANGELOG*' 'CONTRIBUTORS*' 'CONTRIBUTING*'
    do
        find "$PACKAGE_INSTALLING_SRC_DIR" -mindepth 1 -maxdepth 1 \( -type f -or -type l \) -iname "$item" -exec cp -L {} "$PACKAGE_METAINFO_DIR/" \;
    done

    #########################################################################################

    [ -n "$PACKAGE_EXECUTABLES_NEED_SET_RPATH_S1" ] && {
        step "set rpath for executables"

        PACKAGE_EXECUTABLES_NEED_SET_RPATH_S1="$(printf '%s\n' $PACKAGE_EXECUTABLES_NEED_SET_RPATH_S1 | sort | uniq)"

        for f in $PACKAGE_EXECUTABLES_NEED_SET_RPATH_S1
        do
            DIRNAME="${f%/*}"
            RELATIVE_PATH="$(realpath -m --relative-to="$DIRNAME" lib)"
            run patchelf --set-rpath "\\\$ORIGIN/$RELATIVE_PATH" "$f"
        done
    }

    [ -n "$PACKAGE_EXECUTABLES_NEED_SET_RPATH_S2" ] && {
        step "set rpath for executables"

        PACKAGE_EXECUTABLES_NEED_SET_RPATH_S2="$(printf '%s\n' $PACKAGE_EXECUTABLES_NEED_SET_RPATH_S2 | sort | uniq)"

        for f in $PACKAGE_EXECUTABLES_NEED_SET_RPATH_S2
        do
            DIRNAME="${f%/*}"
            RELATIVE_PATH="$(realpath -m --relative-to="$DIRNAME" .ndk-pkg/dependencies/lib)"
            run patchelf --set-rpath "\\\$ORIGIN/$RELATIVE_PATH" "$f"
        done
    }

    #########################################################################################

    [ -n "$PACKAGE_DEP_PKG" ] && {
        step "install dependency graph files"

        run mv "$PACKAGE_WORKING_DIR/dependencies.*" "$PACKAGE_METAINFO_DIR/"

        step "install dependency formulas"

        install -d "$PACKAGE_METAINFO_DIR/dependencies"

        for DEPENDENT_PACKAGE_NAME in $RECURSIVE_DEPENDENT_PACKAGE_NAMES
        do
            cp "$SESSION_DIR/$DEPENDENT_PACKAGE_NAME.yml" "$PACKAGE_METAINFO_DIR/dependencies/"
        done
    }

    #########################################################################################

    for dir in "$PACKAGE_BCACHED_DIR" "$PACKAGE_BSCRIPT_DIR"
    do
        if [ -f "$dir/config.log" ] ; then
            mv  "$dir/config.log" "$PACKAGE_METAINFO_DIR/"
        fi
    done

    #########################################################################################

    for dir in "$PACKAGE_BCACHED_DIR" "$PACKAGE_BSCRIPT_DIR"
    do
        if [ -f "$dir/compile_commands.json" ] ; then
            mv  "$dir/compile_commands.json" "$PACKAGE_METAINFO_DIR/"
        fi
    done

    #########################################################################################

    mv "$TOOLCHAIN_CONFIG_NATIVE" "$PACKAGE_METAINFO_DIR/"
    mv "$TOOLCHAIN_CONFIG_TARGET" "$PACKAGE_METAINFO_DIR/"

    #########################################################################################

    [ -n "$PACKAGE_ONFINAL" ] && {
        step "onfinal"

        cd "$PACKAGE_INSTALL_DIR"

        eval "
onfinal() {
$PACKAGE_ONFINAL
}"
        onfinal
    }

    #########################################################################################

    cd "$PACKAGE_INSTALL_DIR"

    step "generate MANIFEST.txt"
    __generate_manifest_of_the_given_package "$1"

    step "generate RECEIPT.yml"
    __generate_receipt_of_the_given_package "$1"

    step "generate index"
    run ln -s -r -f -T "$PACKAGE_INSTALL_DIR" "$NDKPKG_PACKAGE_INSTALLED_ROOT/$PACKAGE_SPEC"

    #########################################################################################

    [ "$ENABLE_CCACHE" = 1 ] && {
        step "show ccache statistics summary"
        note "Before Build:"
        run  cat "$PACKAGE_WORKING_DIR/ccache-s.txt"
        note "After  Build:"
        run  ccache -s
    }

    [ "$REQUEST_TO_KEEP_SESSION_DIR" != 1 ] && {
        step "delete the working directory"
        run rm -rf "$PACKAGE_WORKING_DIR"
    }

    #########################################################################################

    step "show installed files in tree-like format"
    run tree --dirsfirst -a "$PACKAGE_INSTALL_DIR"

    #########################################################################################

    printf '\n%b\n' "${COLOR_PURPLE}✅️  ${COLOR_OFF}${COLOR_GREEN}${1} was successfully installed.${COLOR_OFF}${COLOR_PURPLE}${COLOR_OFF}"

    if [ -n "$PACKAGE_CAVEATS" ] ; then
        printf '\n%b\n' "${COLOR_YELLOW}⚠️  Caveats:\n\n$PACKAGE_CAVEATS${COLOR_OFF}" >&2
    fi
}

__check_elf_files() {
    cd "$PACKAGE_INSTALL_DIR"

    FILEPATHs="$(find -not -name . -type f)"

    export IFS='
'

    for FILEPATH in $FILEPATHs
    do
        [ -f "$FILEPATH" ] || continue

        FILE_HEADER_ACTUAL="$(xxd -u -p -l 20 "$FILEPATH")"

        # http://www.sco.com/developers/gabi/latest/ch4.eheader.html
        case $FILE_HEADER_ACTUAL in
            7F454C46*)
                ELF_TYPE="$(printf '%s\n' "$FILE_HEADER_ACTUAL" | cut -c33-34)"

                case $TARGET_PLATFORM_ARCH in
                    armv7a)  FILE_HEADER_EXPACT="7F454C46010101000000000000000000${ELF_TYPE}002800" ;;
                    aarch64) FILE_HEADER_EXPACT="7F454C46020101000000000000000000${ELF_TYPE}00B700" ;;
                    i686)    FILE_HEADER_EXPACT="7F454C46010101000000000000000000${ELF_TYPE}000300" ;;
                    x86_64)  FILE_HEADER_EXPACT="7F454C46020101000000000000000000${ELF_TYPE}003E00" ;;
                esac

                if [ "$FILE_HEADER_EXPACT" != "$FILE_HEADER_ACTUAL" ] ; then
                    abort 1 "ELF file header mismatch: $FILEPATH\n    expect : $FILE_HEADER_EXPACT\n    actual : $FILE_HEADER_ACTUAL"
                fi

                #####################################################################

                # 03 means it is a shared library or dynamically linked executable
                if [ "$ELF_TYPE" = '03' ] ; then
                    # patchelf would report a error if there is no '.interp' section in a ELF file
                    PT_INTERP="$(patchelf --print-interpreter "$FILEPATH" 2>/dev/null || true)"

                    if [ -n  "$PT_INTERP" ] ; then
                        if [ "$PT_INTERP" != "$ANDROID_DYNAMIC_LINKER_PATH" ] ; then
                            abort 1 "ELF file PT_INTERP mismatch: $FILEPATH\n    expect : $ANDROID_DYNAMIC_LINKER_PATH\n    actual : $PT_INTERP"
                        fi

                        # https://source.android.com/security/enhancements/enhancements50
                        # Android 5.0 and later, a dynamically linked executable must be a PIE(Position Independent Executable)
                        if ! "$READELF" -d "$FILEPATH" | grep FLAGS_1 | grep -q PIE ; then
                            abort 1 "invalid ELF file: $FILEPATH\n    Android 5.0 and later requires dynamically linked executable to support PIE (position-independent executables)."
                        fi

                        # A dynamically linked executable always have the .interp section.
                        # In general, a shared library would not have the .interp section, but that is not always the case, for example, libcap/lib/libpsx.so is not only a library but also a executable.
                        case "${FILEPATH##*/}" in
                            *.so)
                                IS_SHARED_LIBRAY=1 ;;
                            *.so.*)
                                IS_SHARED_LIBRAY=1 ;;
                            *)  IS_SHARED_LIBRAY=0 ;;
                        esac
                    else
                        IS_SHARED_LIBRAY=1
                    fi

                    #####################################################################

                    if [ "$IS_SHARED_LIBRAY" = 1 ] ; then
                        # http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#dynamic_section
                        DT_SONAME="$(patchelf --print-soname "$FILEPATH")"

                        # On Android, a so file must have the DT_SONAME
                        # https://github.com/android/ndk/issues/1865
                        # https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md
                        if [ -z "$DT_SONAME" ] ; then
                            abort 1 "invalid .so file: $FILEPATH\n    no DT_SONAME was set in .dynamic section"
                        fi

                        IS_EXECUTABLE=0
                    else
                        IS_EXECUTABLE=1
                    fi

                    #####################################################################

                    __check_DT_NEEDED "$FILEPATH" "$IS_EXECUTABLE"
                fi
        esac
    done

    unset IFS
}

# __check_DT_NEEDED <ELF-FILE-PATH> <IS-EXECUTABLE>
  __check_DT_NEEDED() {
    # a \n-separated list
    DT_NEEDED_LIST="$(patchelf --print-needed "$1")"

    for DT_NEEDED in $DT_NEEDED_LIST
    do
        case $DT_NEEDED in
            libc.so)
                ;;
            libm.so)
                ;;
            libdl.so)
                ;;
            libomp.so)
                ;;
            liblog.so)
                ;;
            libandroid.so)
                ;;
            libmediandk.so)
                ;;
            libcamera2ndk.so)
                ;;
            libjnigraphics.so)
                ;;
            libOpenSLES.so)
                ;;
            libOpenMAXAL.so)
                ;;
            libEGL.so)
                ;;
            libGLESv1_CM.so)
                ;;
            libGLESv2.so)
                ;;
            libGLESv3.so)
                ;;
            libstdc++.so)
                ;;
            libc++_shared.so)
                ;;
            libclang_rt.*.so)
                ;;
            lib*.so)
                DT_NEEDED_FILEPATH="$PACKAGE_INSTALL_DIR/lib/$DT_NEEDED"

                if [ -f "$DT_NEEDED_FILEPATH" ] ; then
                    if [ "$2" = 1 ] ; then
                        PACKAGE_EXECUTABLES_NEED_SET_RPATH_S1="$PACKAGE_EXECUTABLES_NEED_SET_RPATH_S1 $1"
                    fi
                else
                    unset DT_NEEDED_FILEPATH

                    if [ -n "$RECURSIVE_DEPENDENT_PACKAGE_NAMES" ] ; then
                        RECURSIVE_DEPENDENT_PACKAGE_NAMES2="$(printf '%s\n' "$RECURSIVE_DEPENDENT_PACKAGE_NAMES" | tr ' ' '\n')"

                        for DEPENDENT_PACKAGE_NAME in $RECURSIVE_DEPENDENT_PACKAGE_NAMES2
                        do
                            DT_NEEDED_FILEPATH="$NDKPKG_PACKAGE_INSTALLED_ROOT/$TARGET_PLATFORM_SPEC/$DEPENDENT_PACKAGE_NAME/lib/$DT_NEEDED"

                            if [ -f "$DT_NEEDED_FILEPATH" ] ; then
                                if [ !      -d    "$PACKAGE_DEPENDENT_SHARED_LIBS_DIR" ] ; then
                                    install -d -v "$PACKAGE_DEPENDENT_SHARED_LIBS_DIR"
                                fi

                                if [ ! -f "$PACKAGE_DEPENDENT_SHARED_LIBS_DIR/$DT_NEEDED" ] ; then
                                    cp -L -v "$DT_NEEDED_FILEPATH" "$PACKAGE_DEPENDENT_SHARED_LIBS_DIR/"
                                fi

                                if [ "$2" = 1 ] ; then
                                    PACKAGE_EXECUTABLES_NEED_SET_RPATH_S2="$PACKAGE_EXECUTABLES_NEED_SET_RPATH_S2 $1"
                                fi

                                break
                            else
                                unset DT_NEEDED_FILEPATH
                            fi
                        done
                    fi
                fi

                if [ -z "$DT_NEEDED_FILEPATH" ] ; then
                    abort 1 "NEEDED: $DT_NEEDED not found for ELF file: $1"
                else
                    __check_DT_NEEDED "$DT_NEEDED_FILEPATH" 0
                fi
                ;;
            *)  abort 1 "unexpected ELF file: $1\n    invalid DT_NEEDED: $DT_NEEDED"
        esac
    done
}

__tweak_pc_files() {
    unset PC_FILES

    for item in lib share
    do
        PC_FILES_LIVEDIR="$PACKAGE_INSTALL_DIR/$item/pkgconfig"

        if [ -d        "$PC_FILES_LIVEDIR" ] ; then
            fs="$(find "$PC_FILES_LIVEDIR" -type f -name '*.pc')"

            if [ -n "$fs" ] ; then
                PC_FILES="$PC_FILES $fs"
            fi
        fi
    done

    for pcfile in $PC_FILES
    do
        gsed -i "s|$PACKAGE_INSTALL_DIR|\${pcfiledir}/../..|g" "$pcfile"

        gsed -i "s|-I$NDKPKG_HOME[^' ]*||g"   "$pcfile"
        gsed -i "s|-L$NDKPKG_HOME[^' ]*||g"   "$pcfile"
        gsed -i "s|-L$SYSROOT[^' ]*||g"       "$pcfile"
        gsed -i "s|--sysroot=$SYSROOT||"      "$pcfile"
        gsed -i 's|-flto||g'                  "$pcfile"
        gsed -i 's|-lpthread||g'              "$pcfile"
        gsed -i 's|-Wl,--strip-debug||g'      "$pcfile"

        gsed -i "s|${NDKPKG_HOME}/.*/lib\(.*\)\.so|-l\1|g" "$pcfile"
        gsed -i "s|${NDKPKG_HOME}/.*/lib\(.*\)\.a|-l\1|g"  "$pcfile"

        if grep -q 'Libs.private:' "$pcfile" ; then
            LIBS_CONTENT=$(awk '/Libs:/{print}' "$pcfile")
            LIBS_PRIVATE_CONTENT=$(awk -F: '/Libs.private:/{print $2}' "$pcfile")
            gsed -i "s|$LIBS_CONTENT|$LIBS_CONTENT$LIBS_PRIVATE_CONTENT|" "$pcfile"
            gsed -i '/Libs.private/d' "$pcfile"
        fi

        if grep -q 'Requires.private:' "$pcfile" ; then
            if grep -q 'Requires:' "$pcfile" ; then
                REQUIRES_PRIVATE_CONTENT=$(sed -n '/Requires.private:/p' "$pcfile" | cut -c18-)
                gsed -i "/Requires:/s|\$|$REQUIRES_PRIVATE_CONTENT|" "$pcfile"
                gsed -i '/Requires.private:/d' "$pcfile"
            else
                gsed -i 's|Requires.private:|Requires:|' "$pcfile"
            fi
        fi
    done
}

install_incs() {
    while [ -n "$1" ]
    do
        unset X1
        unset X2
        X1=$(printf '%s\n' "$1" | cut -d: -f1)
        X2=$(printf '%s\n' "$1" | cut -d: -f2)

        if [ "$X1" = "$X2" ] ; then
            unset X2
        fi

        install -v -d         "$PACKAGE_INSTALL_DIR/include/$X2"
        install -v -m 644 $X1 "$PACKAGE_INSTALL_DIR/include/$X2"

        shift
    done
}

install_libs() {
    install -v -d "$PACKAGE_INSTALL_DIR/lib"
    for item in "$@"
    do
        case $item in
            *.a) install -v -m 644 "$item" "$PACKAGE_INSTALL_DIR/lib" ;;
            *)   install -v -m 755 "$item" "$PACKAGE_INSTALL_DIR/lib" ;;
        esac
    done
}

writepc() {
    install -v -d "$PACKAGE_INSTALL_DIR/lib/pkgconfig" &&
    cat >         "$PACKAGE_INSTALL_DIR/lib/pkgconfig/$1.pc"
}

install_pcfs() {
    install -v -d          "$PACKAGE_INSTALL_DIR/lib/pkgconfig" &&
    install -v -m 644 "$@" "$PACKAGE_INSTALL_DIR/lib/pkgconfig"
}

install_bins() {
    install -v -d          "$PACKAGE_INSTALL_DIR/bin" &&
    install -v -m 755 "$@" "$PACKAGE_INSTALL_DIR/bin"
}

install_etcs() {
    install -v -d          "$PACKAGE_INSTALL_DIR/etc" &&
    install -v -m 644 "$@" "$PACKAGE_INSTALL_DIR/etc"
}

install_mans() {
    for item in "$@"
    do
        unset NUMBER
        NUMBER=$(printf '%s\n' "$item" | cut -c ${#item}-${#item})
        case $NUMBER in
            [1-8]);;
            *)    abort 1 "$item: not a manpage."
        esac
        install -v -d             "$PACKAGE_INSTALL_DIR/share/man/man$NUMBER" &&
        install -v -m 644 "$item" "$PACKAGE_INSTALL_DIR/share/man/man$NUMBER"
    done
}

# install_completion <fish|bash|zsh> <COMMAND> <FILE-PATH>
  install_completion() {
    case $1 in
        bash)
            install -v -d          "$PACKAGE_INSTALL_DIR/share/bash/completions" &&
            install -v -m 644 "$3" "$PACKAGE_INSTALL_DIR/share/bash/completions/$2"
            ;;
        fish)
            install -v -d          "$PACKAGE_INSTALL_DIR/share/fish/vendor_completions.d" &&
            install -v -m 644 "$3" "$PACKAGE_INSTALL_DIR/share/fish/vendor_completions.d/$2.fish"
            ;;
        zsh)
            install -v -d          "$PACKAGE_INSTALL_DIR/share/zsh/site-functions" &&
            install -v -m 644 "$3" "$PACKAGE_INSTALL_DIR/share/zsh/site-functions/_$2"
            ;;
        *)  abort 1 "install_completion unsupported shell: $1"
    esac
}

__record_installed_files_of_the_given_package() {
    if [ -z "$2" ] ; then
        INSTALLED_FILES_FILEPATH="$PACKAGE_INSTALL_DIR/installed-files"
        printf '%s\n' "-- Installing: $INSTALLED_FILES_FILEPATH"

        touch "$INSTALLED_FILES_FILEPATH"
        return 0

        # TODO
        exec 7> "$INSTALLED_FILES_FILEPATH"

        __record_installed_files_of_the_given_package "$1" "$PACKAGE_INSTALL_DIR"

        exec 7>&-

        gsed -i "s|$PACKAGE_INSTALL_DIR/||" "$INSTALLED_FILES_FILEPATH"
    else
        for file in $(ls $2)
        do
            file="$2/$file"
            if [ -d "$file" ] ; then
                __record_installed_files_of_the_given_package "$1" "$file"
            else
                printf '%s %s\n' $(md5sum "$file") "$file" >&7
            fi
        done
    fi
}

gow() {
    if [ "$VERBOSE_GO" = 1 ] ; then
        run "go env | bat --language=bash --paging=never --style=plain"
    fi

    # https://pkg.go.dev/cmd/go
    # https://pkg.go.dev/cmd/link

    unset GO_BUILD_ARGS
    unset GO_BUILD_ARGV_V
    unset GO_BUILD_ARGV_X
    unset GO_BUILD_ARGV_O
    unset GO_BUILD_ARGV_MOD
    unset GO_BUILD_ARGV_TAGS
    unset GO_BUILD_ARGV_LDFLAGS

    unset GO_BUILD_ARGS_EXTRA

    while [ -n "$1" ]
    do
        case $1 in
            -v) shift ; GO_BUILD_ARGV_V='-v' ;;
            -x) shift ; GO_BUILD_ARGV_X='-x' ;;
            -o) shift ; GO_BUILD_ARGV_O="$1" ; shift ;;
            -X) shift
                if [ -z "$GO_BUILD_ARGV_LDFLAGS" ] ; then
                    GO_BUILD_ARGV_LDFLAGS="-X $1"
                else
                    GO_BUILD_ARGV_LDFLAGS="$GO_BUILD_ARGV_LDFLAGS -X $1"
                fi
                shift
                ;;
            -ldflags)
                shift
                if [ -z "$GO_BUILD_ARGV_LDFLAGS" ] ; then
                    GO_BUILD_ARGV_LDFLAGS="$1"
                else
                    GO_BUILD_ARGV_LDFLAGS="$1 $GO_BUILD_ARGV_LDFLAGS"
                fi
                shift
                ;;
            *)  GO_BUILD_ARGS_EXTRA="$GO_BUILD_ARGS_EXTRA $1" ; shift
        esac
    done

    GO_BUILD_ARGS='-trimpath'

    if [ -z "$GO_BUILD_ARGV_V" ] ; then
        if [ "$VERBOSE_GO" = 1 ] ; then
            GO_BUILD_ARGS="$GO_BUILD_ARGS -v"
        fi
    else
        GO_BUILD_ARGS="$GO_BUILD_ARGS -v"
    fi

    if [ -z "$GO_BUILD_ARGV_X" ] ; then
        if [ "$DEBUG_GO" = 1 ] ; then
            GO_BUILD_ARGS="$GO_BUILD_ARGS -x"
        fi
    else
        GO_BUILD_ARGS="$GO_BUILD_ARGS -x"
    fi

    if [ "$PROFILE" = release ] ; then
        GO_BUILD_ARGV_LDFLAGS="$GO_BUILD_ARGV_LDFLAGS -s -w"
    fi

    if [ "$NATIVE_PLATFORM_KIND" != darwin ] ; then
        if [ "$PACKAGE_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE" = 1 ] && [ "$CGO_ENABLED" -eq 1 ] ; then
            GO_BUILD_ARGV_LDFLAGS="$GO_BUILD_ARGV_LDFLAGS -linkmode external \"-extldflags=-static\""
        fi
    fi

    GO_BUILD_ARGS="$GO_BUILD_ARGS -ldflags '$GO_BUILD_ARGV_LDFLAGS'"

    if [ -z "$GO_BUILD_ARGV_O" ] ; then
        GO_BUILD_ARGS="$GO_BUILD_ARGS -o $PACKAGE_BCACHED_DIR/"
    else
        GO_BUILD_ARGS="$GO_BUILD_ARGS -o $PACKAGE_BCACHED_DIR/$GO_BUILD_ARGV_O"
    fi

    GO_BUILD_ARGS="$GO_BUILD_ARGS $GO_BUILD_ARGS_EXTRA"

    # shellcheck disable=SC2086
    run go build $GO_BUILD_ARGS

    for item in $(ls "$PACKAGE_BCACHED_DIR")
    do
        case $item in
            *.a)  run install_libs "$PACKAGE_BCACHED_DIR/$item" ;;
            *.so) run install_libs "$PACKAGE_BCACHED_DIR/$item" ;;
            *)    run install_bins "$PACKAGE_BCACHED_DIR/$item" ;;
        esac
    done
}

cargow() {
    run rustup target add "$RUST_TARGET"

    case $1 in
        build)
            # https://doc.rust-lang.org/cargo/commands/cargo-clean.html
            # https://doc.rust-lang.org/cargo/commands/cargo-build.html

            unset CARGO_BUILD_ARGS
            unset CARGO_BUILD_ARG_VV
            unset CARGO_BUILD_ARG_TARGET
            unset CARGO_BUILD_ARG_RELEASE

            for arg in $@
            do
                case $arg in
                    --vv)      CARGO_BUILD_ARG_VV=set      ;;
                    --target)  CARGO_BUILD_ARG_TARGET=set  ;;
                    --release) CARGO_BUILD_ARG_RELEASE=set ;;
                esac
            done

            CARGO_BUILD_ARGS="$@"

            if [ -z "$CARGO_BUILD_ARG_VV" ] ; then
                if [ "$DEBUG_CARGO" = 1 ] ; then
                    CARGO_BUILD_ARGS="$CARGO_BUILD_ARGS -vv"
                fi
            fi

            if [ -z "$CARGO_BUILD_ARG_RELEASE" ] ; then
                CARGO_BUILD_ARGS="$CARGO_BUILD_ARGS --release"
            fi

            if [ -z "$CARGO_BUILD_ARG_TARGET" ] ; then
                CARGO_BUILD_ARGS="$CARGO_BUILD_ARGS --target $RUST_TARGET"
            fi

            run cargo clean && run cargo $CARGO_BUILD_ARGS
            ;;
        install)
            # https://doc.rust-lang.org/cargo/commands/cargo-clean.html
            # https://doc.rust-lang.org/cargo/commands/cargo-install.html

            unset CARGO_INSTALL_ARGS
            unset CARGO_INSTALL_ARG_TARGET
            unset CARGO_INSTALL_ARG_PATH
            unset CARGO_INSTALL_ARG_ROOT
            unset CARGO_INSTALL_ARG_VV

            for arg in $@
            do
                case $arg in
                    --target) CARGO_INSTALL_ARG_TARGET=set ;;
                    --path)   CARGO_INSTALL_ARG_PATH=set   ;;
                    --root)   CARGO_INSTALL_ARG_ROOT=set   ;;
                    --vv)     CARGO_INSTALL_ARG_VV=set     ;;
                esac
            done

            CARGO_INSTALL_ARGS="$@"

            if [ -z "$CARGO_BUILD_ARG_VV" ] ; then
                if [ "$DEBUG_CARGO" = 1 ] ; then
                    CARGO_INSTALL_ARGS="$CARGO_INSTALL_ARGS -vv"
                fi
            fi

            if [ -z "$CARGO_INSTALL_ARG_TARGET" ] ; then
                CARGO_INSTALL_ARGS="$CARGO_INSTALL_ARGS --target $RUST_TARGET"
            fi

            if [ -z "$CARGO_INSTALL_ARG_PATH" ] ; then
                CARGO_INSTALL_ARGS="$CARGO_INSTALL_ARGS --path $PACKAGE_BSCRIPT_DIR"
            fi

            if [ -z "$CARGO_INSTALL_ARG_ROOT" ] ; then
                CARGO_INSTALL_ARGS="$CARGO_INSTALL_ARGS --root=$PACKAGE_INSTALL_DIR"
            fi

            run cargo clean && run cargo $CARGO_INSTALL_ARGS
            ;;
        cbuild|cinstall)
            unset CARGO_CINSTALL_ARGS
            unset CARGO_CINSTALL_ARG_Q
            unset CARGO_CINSTALL_ARG_V
            unset CARGO_CINSTALL_ARG_VV
            unset CARGO_CINSTALL_ARG_DEBUG
            unset CARGO_CINSTALL_ARG_RELEASE
            unset CARGO_CINSTALL_ARG_TARGET
            unset CARGO_CINSTALL_ARG_PREFIX

            for arg in $@
            do
                case $arg in
                    -q|--quiet)   CARGO_CINSTALL_ARG_Q=set       ;;
                    -v|--verbose) CARGO_CINSTALL_ARG_V=set       ;;
                    -vv)          CARGO_CINSTALL_ARG_VV=set      ;;
                    --debug)      CARGO_CINSTALL_ARG_DEBUG=set   ;;
                    --release)    CARGO_CINSTALL_ARG_RELEASE=set ;;
                    --target)     CARGO_CINSTALL_ARG_TARGET=set  ;;
                    --prefix)     CARGO_CINSTALL_ARG_PREFIX=set  ;;
                esac
            done

            CARGO_CINSTALL_ARGS="$@"

            if [ -z "$CARGO_CINSTALL_ARG_Q" ] && [ -z "$CARGO_CINSTALL_ARG_V" ] && [ -z "$CARGO_CINSTALL_ARG_VV" ] ; then
                if [ "$DEBUG_CARGO" = 1 ] ; then
                    CARGO_CINSTALL_ARGS="$CARGO_CINSTALL_ARGS -vv"
                fi
            fi

            if [ -z "$CARGO_CINSTALL_ARG_DEBUG" ] && [ -z "$CARGO_CINSTALL_ARG_RELEASE" ] ; then
                CARGO_CINSTALL_ARGS="$CARGO_CINSTALL_ARGS --release"
            fi

            if [ -z "$CARGO_CINSTALL_ARG_TARGET" ] ; then
                CARGO_CINSTALL_ARGS="$CARGO_CINSTALL_ARGS --target $RUST_TARGET"
            fi

            if [ -z "$CARGO_CINSTALL_ARG_PREFIX" ] ; then
                CARGO_CINSTALL_ARGS="$CARGO_CINSTALL_ARGS --prefix $PACKAGE_INSTALL_DIR"
            fi

            run cargo $CARGO_CINSTALL_ARGS
            ;;
        *) cargo $@
    esac
}

# }}}
##############################################################################
# {{{ waf

waf() {
    run python3 ./waf "$@"
}

# }}}
##############################################################################
# {{{ configure

configure() {
    unset CONFIGURE_ONLY

    if [ "$1" = only ] ; then
        CONFIGURE_ONLY=1
        shift
    fi

    if [ "$BUILD_FOR_NATIVE" = 1 ] ; then
        if run "$PACKAGE_BSCRIPT_DIR"/configure \
            --prefix="$PACKAGE_INSTALL_DIR" \
            $@ ; then
            echo
        else
            if [ -f "$PACKAGE_BCACHED_DIR/config.log" ] ; then
                run cat "$PACKAGE_BCACHED_DIR/config.log"
            elif [ -f "$PACKAGE_BSCRIPT_DIR/config.log" ] ; then
                run cat "$PACKAGE_BSCRIPT_DIR/config.log"
            fi
            return 1
        fi
    else
        # https://www.gnu.org/software/autoconf/manual/autoconf-2.71/html_node/Generic-Functions.html

        export ac_cv_search_log=-lm
        export ac_cv_search_ceil=-lm

        export ac_cv_lib_m_sqrt=yes

        export ac_cv_func_pow=yes
        export ac_cv_func_ffsl=yes
        export ac_cv_func_free=yes
        export ac_cv_func_calloc=yes
        export ac_cv_func_malloc=yes
        export ac_cv_func_realloc=yes
        export ac_cv_func_memset=yes
        export ac_cv_func_strchr=yes
        export ac_cv_func_strstr=yes
        export ac_cv_func_stpcpy=yes
        export ac_cv_func_strcpy=yes
        export ac_cv_func_strtol=yes
        export ac_cv_func_strdup=yes
        export ac_cv_func_strndup=yes
        export ac_cv_func_wcslen=yes
        export ac_cv_func_isblank=yes
        export ac_cv_func_strerror=yes
        export ac_cv_func_snprintf=yes
        export ac_cv_func_faccessat=yes
        export ac_cv_func_sigsetmask=no
        export ac_cv_func_strcasecmp=yes
        export ac_cv_func_vsnprintf=yes

        export ac_cv_func_malloc_0_nonnull=yes
        export ac_cv_func_calloc_0_nonnull=yes
        export ac_cv_func_realloc_0_nonnull=yes

        # https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md#is-32_bit-on-lp32-y2038
        if [ "$TARGET_PLATFORM_NBIT" -eq 32 ] ; then
            export ac_year2038_required=no
        fi

        # string.h  void* _Nonnull memset_explicit(void* _Nonnull __dst, int __ch, size_t __n) __INTRODUCED_IN(34);
        # stdio_ext.h  size_t __freadahead(FILE* _Nonnull __fp) __INTRODUCED_IN(34);
        if [ "$TARGET_PLATFORM_VERS" -ge 34 ] ; then
            export ac_cv_func_memset_explicit=yes
            export ac_cv_func___freadahead=yes
        else
            export ac_cv_func_memset_explicit=no
            export ac_cv_func___freadahead=no
        fi

        # sys/stat.h int statx(int __dir_fd, const char* _Nonnull __path, int __flags, unsigned __mask, struct statx* _Nonnull __buf) __INTRODUCED_IN(30);
        if [ "$TARGET_PLATFORM_VERS" -ge 30 ] ; then
            export ac_cv_func_statx=yes
        else
            export ac_cv_func_statx=no
        fi

        # malloc.h  void* _Nullable reallocarray(void* _Nullable __ptr, size_t __item_count, size_t __item_size) __BIONIC_ALLOC_SIZE(2, 3) __wur __INTRODUCED_IN(29);
        if [ "$TARGET_PLATFORM_VERS" -ge 29 ] ; then
            export ac_cv_func_reallocarray=yes
        else
            export ac_cv_func_reallocarray=no
        fi

        # unistd.h     int syncfs(int __fd) __INTRODUCED_IN(28);
        # sys/random.h ssize_t getrandom(void* _Nonnull __buffer, size_t __buffer_size, unsigned int __flags) __wur __INTRODUCED_IN(28);
        # stdio_ext.h  void __fseterr(FILE* _Nonnull __fp) __INTRODUCED_IN(28);
        # stdio_ext.h  int __freading(FILE* _Nonnull __fp) __INTRODUCED_IN(28);
        # stdio_ext.h  int __fwriting(FILE* _Nonnull __fp) __INTRODUCED_IN(28);
        # stdio.h      size_t fwrite_unlocked(const void* _Nonnull __buf, size_t __size, size_t __count, FILE* _Nonnull __fp) __INTRODUCED_IN(28);
        # extern       void* _Nonnull (*volatile _Nonnull __malloc_hook)(size_t __byte_count, const void* _Nonnull __caller) __INTRODUCED_IN(28);
        # spawn.h      int posix_spawnp(pid_t* _Nullable __pid, const char* _Nonnull __file, const posix_spawn_file_actions_t _Nullable * _Nullable __actions, const posix_spawnattr_t _Nullable * _Nullable __attr, char* const _Nonnull __argv[_Nonnull], char* const _Nullable __env[_Nullable]) __INTRODUCED_IN(28);
        if [ "$TARGET_PLATFORM_VERS" -ge 28 ] ; then
            export ac_cv_func_syncfs=yes
            export ac_cv_func_getrandom=yes
            export ac_cv_func___fseterr=yes
            export ac_cv_func___freading=yes
            export ac_cv_func___fwriting=yes
            export ac_cv_func___malloc_hook=yes
            export ac_cv_func_fwrite_unlocked=yes
            export ac_cv_func_posix_spawnp=yes
            export ac_cv_func_posix_spawn_file_actions_init=yes
            export ac_cv_func_posix_spawn_file_actions_addclose=yes
            export ac_cv_func_posix_spawn_file_actions_adddup2=yes
            export ac_cv_func_posix_spawn_file_actions_destroy=yes
        else
            export ac_cv_func_syncfs=no
            export ac_cv_func_getrandom=no
            export ac_cv_func___fseterr=no
            export ac_cv_func___freading=no
            export ac_cv_func___fwriting=no
            export ac_cv_func___malloc_hook=no
            export ac_cv_func_fwrite_unlocked=no
            export ac_cv_func_posix_spawnp=no
            export ac_cv_func_posix_spawn_file_actions_init=no
            export ac_cv_func_posix_spawn_file_actions_addclose=no
            export ac_cv_func_posix_spawn_file_actions_adddup2=no
            export ac_cv_func_posix_spawn_file_actions_destroy=no
        fi

        # stdio.h     char* _Nonnull ctermid(char* _Nullable __buf) __INTRODUCED_IN(26);
        # sys/time.h  int futimesat(int __dir_fd, const char* __BIONIC_COMPLICATED_NULLNESS __path, const struct timeval __times[_Nullable 2]) __INTRODUCED_IN(26);
        # sys/time.h  int lutimes(const char* _Nonnull __path, const struct timeval __times[_Nullable 2]) __INTRODUCED_IN(26);
        # langinfo.h  char* _Nonnull nl_langinfo(nl_item __item) __INTRODUCED_IN(26);
        # pwd.h  struct passwd* getpwent(void) __INTRODUCED_IN(26);
        if [ "$TARGET_PLATFORM_VERS" -ge 26 ] ; then
            export ac_cv_func_futimesat=yes
            export ac_cv_func_futimes=yes
            export ac_cv_func_lutimes=yes
            export ac_cv_func_ctermid=yes
            export ac_cv_func_setpwent=yes
            export ac_cv_func_getpwent=yes
            export ac_cv_func_endpwent=yes
            export ac_cv_func_nl_langinfo=yes
        else
            export ac_cv_func_futimesat=no
            export ac_cv_func_futimes=no
            export ac_cv_func_lutimes=no
            export ac_cv_func_ctermid=no
            export ac_cv_func_setpwent=no
            export ac_cv_func_getpwent=no
            export ac_cv_func_endpwent=no
            export ac_cv_func_nl_langinfo=no
        fi

        # grp.h  int getgrnam_r(const char* _Nonnull __name, struct group* __BIONIC_COMPLICATED_NULLNESS __group, char* _Nonnull __buf, size_t __n, struct group* _Nullable *_Nonnull __result) __INTRODUCED_IN(24);
        # grp.h  int getgrgid_r(gid_t __gid, struct group* __group, char* __buf, size_t __n, struct group** __result) __INTRODUCED_IN(24);
        if [ "$TARGET_PLATFORM_VERS" -ge 24 ] ; then
            export ac_cv_func_getgrnam_r=yes
            export ac_cv_func_getgrgid_r=yes
        else
            export ac_cv_func_getgrnam_r=no
            export ac_cv_func_getgrgid_r=no
        fi

        # stdio_ext.h  int __fsetlocking(FILE* _Nonnull __fp, int __type) __INTRODUCED_IN(23);
        # string.h     char* _Nonnull strerror_l(int __errno_value, locale_t _Nonnull __l) __INTRODUCED_IN(23);
        # stdlib.h     int mkostemp(char* _Nonnull __template, int __flags) __INTRODUCED_IN(23);
        # stdio.h      FILE* _Nullable fmemopen(void* _Nullable __buf, size_t __size, const char* _Nonnull __mode) __INTRODUCED_IN(23);
        if [ "$TARGET_PLATFORM_VERS" -ge 23 ] ; then
            export ac_cv_func_mempcpy=yes
            export ac_cv_func_wmempcpy=yes
            export ac_cv_func_mkostemp=yes
            export ac_cv_func_fmemopen=yes
            export ac_cv_func_strerror_l=yes
            export ac_cv_func___fsetlocking=yes
        else
            export ac_cv_func_mempcpy=no
            export ac_cv_func_wmempcpy=no
            export ac_cv_func_mkostemp=no
            export ac_cv_func_fmemopen=no
            export ac_cv_func_strerror_l=no
            export ac_cv_func___fsetlocking=no
        fi

        CONFIGURE_ARG_ENABLE_NLS=0
        CONFIGURE_ARG_ENABLE_RPATH=0
        CONFIGURE_ARG_ENABLE_LARGEFILE=1

        CONFIGURE_ARG_ENABLE_DEBUG=
        CONFIGURE_ARG_ENABLE_STATIC=
        CONFIGURE_ARG_ENABLE_SHARED=

        for arg in "$@"
        do
            case $arg in
                --enable-nls)      CONFIGURE_ARG_ENABLE_NLS=1 ;;
                --enable-nls=yes)  CONFIGURE_ARG_ENABLE_NLS=1 ;;
                --enable-nls=no)   CONFIGURE_ARG_ENABLE_NLS=0 ;;
                --disable-nls)     CONFIGURE_ARG_ENABLE_NLS=0 ;;

                --enable-rpath)     CONFIGURE_ARG_ENABLE_RPATH=1 ;;
                --enable-rpath=yes) CONFIGURE_ARG_ENABLE_RPATH=1 ;;
                --enable-rpath=no)  CONFIGURE_ARG_ENABLE_RPATH=0 ;;
                --disable-rpath)    CONFIGURE_ARG_ENABLE_RPATH=0 ;;

                --enable-largefile)     CONFIGURE_ARG_ENABLE_LARGEFILE=1 ;;
                --enable-largefile=yes) CONFIGURE_ARG_ENABLE_LARGEFILE=1 ;;
                --enable-largefile=no)  CONFIGURE_ARG_ENABLE_LARGEFILE=0 ;;
                --disable-largefile)    CONFIGURE_ARG_ENABLE_LARGEFILE=0 ;;

                --enable-debug)     CONFIGURE_ARG_ENABLE_DEBUG=1 ;;
                --enable-debug=yes) CONFIGURE_ARG_ENABLE_DEBUG=1 ;;
                --enable-debug=no)  CONFIGURE_ARG_ENABLE_DEBUG=0 ;;
                --disable-debug)    CONFIGURE_ARG_ENABLE_DEBUG=0 ;;

                --enable-static)     CONFIGURE_ARG_ENABLE_STATIC=1 ;;
                --enable-static=yes) CONFIGURE_ARG_ENABLE_STATIC=1 ;;
                --enable-static=no)  CONFIGURE_ARG_ENABLE_STATIC=0 ;;
                --disable-static)    CONFIGURE_ARG_ENABLE_STATIC=0 ;;

                --enable-shared)     CONFIGURE_ARG_ENABLE_SHARED=1 ;;
                --enable-shared=yes) CONFIGURE_ARG_ENABLE_SHARED=1 ;;
                --enable-shared=no)  CONFIGURE_ARG_ENABLE_SHARED=0 ;;
                --disable-shared)    CONFIGURE_ARG_ENABLE_SHARED=0 ;;
            esac
        done

        CONFIGURE_ARGS="--host='$TARGET_TRIPLE' --prefix='$PACKAGE_INSTALL_DIR' --disable-option-checking"

        if [ "$CONFIGURE_ARG_ENABLE_NLS" = 1 ] ; then
            CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-nls"
        else
            CONFIGURE_ARGS="$CONFIGURE_ARGS --disable-nls"
        fi

        if [ "$CONFIGURE_ARG_ENABLE_RPATH" = 1 ] ; then
            CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-rpath"
        else
            CONFIGURE_ARGS="$CONFIGURE_ARGS --disable-rpath"
        fi

        if [ "$CONFIGURE_ARG_ENABLE_LARGEFILE" = 1 ] ; then
            CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-largefile"
        else
            CONFIGURE_ARGS="$CONFIGURE_ARGS --disable-largefile"
        fi

        if [ -z "$CONFIGURE_ARG_ENABLE_DEBUG" ] ; then
            case $PROFILE in
                debug)   CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-debug"  ;;
                release) CONFIGURE_ARGS="$CONFIGURE_ARGS --disable-debug" ;;
            esac
        fi

        if [ -z "$CONFIGURE_ARG_ENABLE_STATIC" ] ; then
            CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-static"
        fi

        if [ -z "$CONFIGURE_ARG_ENABLE_SHARED" ] ; then
            if [ "$PACKAGE_PKGTYPE" = exe ] || [ "$PACKAGE_PKGTYPE" = pie ] ; then
                CONFIGURE_ARGS="$CONFIGURE_ARGS --disable-shared"
            else
                CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-shared"
            fi
        fi

        # https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md#is-32_bit-on-lp32-y2038
        if [ "$TARGET_PLATFORM_NBIT" = 32 ] ; then
            CONFIGURE_ARGS="$CONFIGURE_ARGS --disable-year2038"
        fi

        CONFIGURE_ENVS="$CONFIGURE_ENVS --with-pic
            CC='$CC'
            CFLAGS='$CFLAGS'
            CXX='$CXX'
            CXXFLAGS='$CXXFLAGS'
            CPP='$CPP'
            CPPFLAGS='$CPPFLAGS'
            LDFLAGS='$LDFLAGS'
            AR='$AR'
            RANLIB='$RANLIB'
            PKG_CONFIG='$PKG_CONFIG'
            PKG_CONFIG_PATH='$PKG_CONFIG_PATH'
            PKG_CONFIG_LIBDIR='$PKG_CONFIG_LIBDIR'
            CC_FOR_BUILD='$CC_FOR_BUILD'"

        CONFIGURE="$PACKAGE_BSCRIPT_DIR/configure"

        gsed -i 's/cross_compiling=no/cross_compiling=yes/g' "$CONFIGURE"

        if run $CONFIGURE $CONFIGURE_ARGS $@ $CONFIGURE_ENVS ; then
            echo
        else
            # https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
            if [ "$GITHUB_ACTIONS" = true ] ; then
                if [ -f "$PACKAGE_BCACHED_DIR/config.log" ] ; then
                    run cat "$PACKAGE_BCACHED_DIR/config.log"
                elif [ -f "$PACKAGE_BSCRIPT_DIR/config.log" ] ; then
                    run cat "$PACKAGE_BSCRIPT_DIR/config.log"
                fi
            fi
            return 1
        fi
    fi

    if [ "$VERBOSE_GMAKE" = 1 ] ; then
        for Makefile in $(find "$PACKAGE_BSCRIPT_DIR" -name Makefile)
        do
            gsed -i 's|\t@|\t|g'     "$Makefile"
            gsed -i 's|@echo|echo|g' "$Makefile"
        done
        unset Makefile
    fi

    if [ "$CONFIGURE_ONLY" != 1 ] ; then
        gmakew clean
        gmakew
        gmakew install
    fi
}

# gmake wrapper
gmakew() {
    unset GMAKE_OPTIONS
    unset GMAKE_OPTION_SET_C
    unset GMAKE_OPTION_SET_w
    unset GMAKE_OPTION_SET_j

    for option in $@
    do
        case $option in
            -C)           GMAKE_OPTION_SET_C=1 ;;
            -w)           GMAKE_OPTION_SET_w=1 ;;
            -j)           GMAKE_OPTION_SET_j=1 ;;
            -j[1-9])      GMAKE_OPTION_SET_j=1 ;;
            -j[1-9][0-9]) GMAKE_OPTION_SET_j=1 ;;
        esac
    done

    if [ "$GMAKE_OPTION_SET_w" != 1 ] ; then
        GMAKE_OPTIONS="$GMAKE_OPTIONS -w"
    fi

    if [ "$GMAKE_OPTION_SET_C" != 1 ] ; then
        if [ "$PACKAGE_BINBSTD" != 1 ] ; then
            GMAKE_OPTIONS="$GMAKE_OPTIONS -C $PACKAGE_BCACHED_DIR"
        fi
    fi

    if [ "$GMAKE_OPTION_SET_j" != 1 ] ; then
        GMAKE_OPTIONS="$GMAKE_OPTIONS -j$BUILD_NJOBS"
    fi

    if [ "$VERBOSE_GMAKE" = 1 ] ; then
        GMAKE_OPTIONS="$GMAKE_OPTIONS V=1"
    fi

    if [ "$DEBUG_GMAKE" = 1 ] ; then
        GMAKE_OPTIONS="$GMAKE_OPTIONS --debug"
    fi

    if [ "$REQUEST_TO_EXPORT_COMPILE_COMMANDS_JSON" = 1 ] && [ "$BEAR_ENABLED" = 1 ] ; then
        run bear -- $GMAKE $GMAKE_OPTIONS $*
    else
        run         $GMAKE $GMAKE_OPTIONS $*
    fi
}

# cmakew [2] [--targets=<comma-separated list>] [--components=<comma-separated list>] [CMAKE-OPTIONS]
cmakew() {
    unset CMAKE_EXTRA_ARGS
    unset CMAKE_BUILD_TARGETS
    unset CMAKE_INSTALL_COMPONENTS
    unset CMAKE_BUILD_STATIC_SHARED_LIBRARY_SEPARATEDLY

    if [ "$1" = 2 ] ; then
        CMAKE_BUILD_STATIC_SHARED_LIBRARY_SEPARATEDLY=1
        shift
    fi

    while [ -n "$1" ]
    do
        case $1 in
            -S) shift; PACKAGE_BSCRIPT_DIR="$1" ;;
            -B) shift; PACKAGE_BCACHED_DIR="$1" ;;
            --targets=*)
                TARGETS="${1#*=}"

                export IFS=','

                for item in $TARGETS
                do
                    CMAKE_BUILD_TARGETS="$CMAKE_BUILD_TARGETS $item"
                done

                unset IFS
                ;;
            --components=*)
                COMPONENTS="${1#*=}"

                export IFS=','

                for item in $COMPONENTS
                do
                    CMAKE_INSTALL_COMPONENTS="$CMAKE_INSTALL_COMPONENTS $item"
                done

                unset IFS
                ;;
            *)  CMAKE_EXTRA_ARGS="$CMAKE_EXTRA_ARGS $1"
        esac
        shift
    done

    if [ "$CMAKE_BUILD_STATIC_SHARED_LIBRARY_SEPARATEDLY" = 1 ] ; then
        cmakew_internal $CMAKE_EXTRA_ARGS -DBUILD_SHARED_LIBS=OFF
        cmakew_internal $CMAKE_EXTRA_ARGS -DBUILD_SHARED_LIBS=ON
    else
        cmakew_internal $CMAKE_EXTRA_ARGS
    fi
}

# https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling
# https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html
# https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_ROOT_PATH.html
# https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html
# https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html
# https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html
# https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html
# https://cmake.org/cmake/help/latest/command/enable_testing.html?highlight=build_testing
# https://developer.android.com/ndk/guides/cmake
# run in a subshell
cmakew_internal() {
    if [ "$BUILD_FOR_NATIVE" = 1 ] ; then
        CMAKE_CONFIG_OPTIONS="-Wno-dev -DCMAKE_EXPORT_COMPILE_COMMANDS=$CMAKE_EXPORT_COMPILE_COMMANDS -DBUILD_TESTING=OFF"

        # https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_DEBUG_MODE.html
        if [ "$DEBUG_CMAKE" = 1 ] ; then
            CMAKE_CONFIG_OPTIONS="$CMAKE_CONFIG_OPTIONS -DCMAKE_FIND_DEBUG_MODE=TRUE"
        fi

        CMAKE_CONFIG_OPTIONS="$CMAKE_CONFIG_OPTIONS -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX='$PACKAGE_INSTALL_DIR' -S $PACKAGE_BSCRIPT_DIR -B $PACKAGE_BCACHED_DIR"

        run $CMAKE $CMAKE_CONFIG_OPTIONS $@ &&
        run $CMAKE --build   "$PACKAGE_BCACHED_DIR" -- -j$BUILD_NJOBS &&
        run $CMAKE --install "$PACKAGE_BCACHED_DIR"
    else
        unset CMAKE_PROJECT_INCLUDE

        if [ "$PACKAGE_PKGTYPE" = exe ] || [ "$PACKAGE_PKGTYPE" = pie ] ; then
            # https://cmake.org/cmake/help/latest/variable/CMAKE_PROJECT_INCLUDE.html
            CMAKE_PROJECT_INCLUDE="$PACKAGE_WORKING_DIR/project-after.cmake"

            # https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_LIBRARY_SUFFIXES.html
            if [ "$PACKAGE_CREATE_FULLY_STATICALLY_LINKED_EXECUTABLE" = 1 ] ; then
                printf 'set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")\n'       > "$CMAKE_PROJECT_INCLUDE"
            else
                printf 'set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".so")\n' > "$CMAKE_PROJECT_INCLUDE"
            fi

            if [ "$VERBOSE_CMAKE" = 1 ] ; then
                run cat "$CMAKE_PROJECT_INCLUDE"
            fi
        fi

        case $PROFILE in
            debug)   CMAKE_BUILD_TYPE=Debug   ;;
            release) CMAKE_BUILD_TYPE=Release ;;
        esac

        if [ "$VERBOSE_CMAKE" = 1 ] ; then
            CMAKE_VERBOSE_MAKEFILE=ON
            CMAKE_COLOR_MAKEFILE=ON
        else
            CMAKE_VERBOSE_MAKEFILE=OFF
            CMAKE_COLOR_MAKEFILE=OFF
        fi

        # https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_DEBUG_MODE.html
        if [ "$DEBUG_CMAKE" = 1 ] ; then
            CMAKE_FIND_DEBUG_MODE=ON
        else
            CMAKE_FIND_DEBUG_MODE=OFF
        fi

        case $TARGET_PLATFORM_ARCH in
            armv7a)  CMAKE_SYSTEM_PROCESSOR=armv7-a ;;
            aarch64) CMAKE_SYSTEM_PROCESSOR=aarch64 ;;
            i686)    CMAKE_SYSTEM_PROCESSOR=i686    ;;
            x86_64)  CMAKE_SYSTEM_PROCESSOR=x86_64  ;;
        esac

        CMAKE_TOOLCHAIN_FILE="$PACKAGE_WORKING_DIR/android.toolchain.cmake"

        cat > "$CMAKE_TOOLCHAIN_FILE" <<EOF
message(STATUS "CMake command: \${CMAKE_COMMAND}")
message(STATUS "CMake version: \${CMAKE_VERSION}")

if ("\${BUILD_SHARED_LIBS}" STREQUAL "")
    set(BUILD_SHARED_LIBS Release)
endif()

set(ANDROID_ABI "${TARGET_PLATFORM_ABI}")
set(ANDROID_PLATFORM "android-${TARGET_PLATFORM_VERS}")
set(ANDROID_TOOLCHAIN clang)
set(ANDROID_ARM_NEON TRUE)
set(ANDROID_STL c++_shared)
include(${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake)

message(STATUS "CMAKE_HOST_SYSTEM_NAME: \${CMAKE_HOST_SYSTEM_NAME}")
message(STATUS "     CMAKE_SYSTEM_NAME: \${CMAKE_SYSTEM_NAME}")

set(CMAKE_BUILD_TYPE  $CMAKE_BUILD_TYPE)

set(CMAKE_C_COMPILER "$CC")
set(CMAKE_C_FLAGS "$CPPFLAGS $CFLAGS")

set(CMAKE_CXX_COMPILER "$CXX")
set(CMAKE_CXX_FLAGS "$CPPFLAGS $CXXFLAGS")

set(CMAKE_ASM_COMPILER "$CC")

set(CMAKE_SHARED_LINKER_FLAGS "$(printf '%s\n' "$LDFLAGS" | sed -e 's|--static||g' -e 's|-static||g')")
set(CMAKE_EXE_LINKER_FLAGS    "$LDFLAGS")

set(CMAKE_C_COMPILER_AR     "$AR")
set(CMAKE_C_COMPILER_RANLIB "$RANLIB")

set(CMAKE_CXX_COMPILER_AR     "$AR")
set(CMAKE_CXX_COMPILER_RANLIB "$RANLIB")

set(CMAKE_AR      "$AR")
set(CMAKE_RANLIB  "$RANLIB")

set(CMAKE_LINKER  "$LD")

set(CMAKE_NM      "$NM")
set(CMAKE_READELF "$READELF")

set(CMAKE_OBJCOPY "$OBJCOPY")
set(CMAKE_OBJDUMP "$OBJDUMP")

set(CMAKE_STRIP   "$STRIP")

set(CMAKE_ADDR2LINE "$ADDR2LINE")

if ("\${CMAKE_EXE_LINKER_FLAGS}" MATCHES ".*-static.*")
    set(CMAKE_SKIP_INSTALL_RPATH ON)
endif()

set(CMAKE_FIND_DEBUG_MODE $CMAKE_FIND_DEBUG_MODE)
set(CMAKE_FIND_ROOT_PATH "$CMAKE_FIND_ROOT_PATH")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

set(CMAKE_INSTALL_MESSAGE "$CMAKE_INSTALL_MESSAGE")
EOF

        NASM="$(command -v nasm || command -v yasm || true)"

        if [ -n "$NASM" ] ; then
            cat >> "$CMAKE_TOOLCHAIN_FILE" <<EOF
set(CMAKE_ASM_NASM_COMPILER "$NASM")
EOF
        fi

        if [ "$ENABLE_CCACHE" = 1 ] ; then
            cat >> "$CMAKE_TOOLCHAIN_FILE" <<EOF
set(NDK_CCACHE "$CCACHE")
EOF
        fi

        if [ "$VERBOSE_CMAKE" = 1 ] ; then
            bat --language=cmake --paging=never "$CMAKE_TOOLCHAIN_FILE"
        fi

        if [   -f "$PACKAGE_BCACHED_DIR/CMakeCache.txt" ] ; then
            rm -f "$PACKAGE_BCACHED_DIR/CMakeCache.txt"
        fi

        CMAKE_CONFIG_OPTIONS="-Wno-dev -DBUILD_TESTING=OFF -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_INSTALL_PREFIX='$PACKAGE_INSTALL_DIR' -DCMAKE_TOOLCHAIN_FILE='$CMAKE_TOOLCHAIN_FILE' -DCMAKE_VERBOSE_MAKEFILE='$CMAKE_VERBOSE_MAKEFILE' -DCMAKE_COLOR_MAKEFILE='$CMAKE_COLOR_MAKEFILE'"

        if [ -n "$CMAKE_PROJECT_INCLUDE" ] && [ -f "$CMAKE_PROJECT_INCLUDE" ] ; then
            CMAKE_CONFIG_OPTIONS="$CMAKE_CONFIG_OPTIONS -DCMAKE_PROJECT_INCLUDE=$CMAKE_PROJECT_INCLUDE"
        fi

        run "$CMAKE" "$CMAKE_CONFIG_OPTIONS" -S "$PACKAGE_BSCRIPT_DIR" -B "$PACKAGE_BCACHED_DIR" "$@"

        if [ -z "$CMAKE_BUILD_TARGETS" ] ; then
            run "$CMAKE" --build   "$PACKAGE_BCACHED_DIR"
        else
            run "$CMAKE" --build   "$PACKAGE_BCACHED_DIR" --target "$CMAKE_BUILD_TARGETS"
        fi

        if [ -z "$CMAKE_INSTALL_COMPONENTS" ] ; then
            run "$CMAKE" --install "$PACKAGE_BCACHED_DIR"
        else
            for component in $CMAKE_INSTALL_COMPONENTS
            do
            run "$CMAKE" --install "$PACKAGE_BCACHED_DIR" --component "$component"
            done
        fi
    fi
}

# https://github.com/xmake-io/xmake/issues/2003
# run in a subshell
xmakew() {
    XMAKE_CONFIG_OPTIONS="$@"

    XMAKE_CONFIG_OPTION_CLEAN=
    XMAKE_CONFIG_OPTION_MODE=
    XMAKE_CONFIG_OPTION_vD=

    for arg in $@
    do
        case $arg in
            -c|--clean)  XMAKE_CONFIG_OPTION_CLEAN=set ;;
            -m|--mode=*) XMAKE_CONFIG_OPTION_MODE=set  ;;
            -vD)         XMAKE_CONFIG_OPTION_vD=set ;;
        esac
    done

    if [ "$DEBUG_XMAKE" = 1 ] && [ -z "$XMAKE_CONFIG_OPTION_vD" ] ; then
        XMAKE_CONFIG_OPTIONS="$XMAKE_CONFIG_OPTION_vD -vD"
    fi

    if [ -z "$XMAKE_CONFIG_OPTION_CLEAN" ] ; then
        XMAKE_CONFIG_OPTIONS="$XMAKE_CONFIG_OPTIONS --clean"
    fi

    if [ -z "$XMAKE_CONFIG_OPTION_MODE" ] ; then
        XMAKE_CONFIG_OPTIONS="$XMAKE_CONFIG_OPTIONS --mode=$PROFILE"
    fi

    if [ "$BUILD_FOR_NATIVE" = 1 ] ; then
        run $XMAKE config $XMAKE_CONFIG_OPTIONS --project=$PACKAGE_BSCRIPT_DIR --buildir=$PACKAGE_BCACHED_DIR &&
        run $XMAKE --jobs=$BUILD_NJOBS &&
        run $XMAKE install -o "$PACKAGE_INSTALL_DIR"
    else
        XMAKE_CONFIG_OPTIONS="$XMAKE_CONFIG_OPTIONS --plat=android --arch=$TARGET_PLATFORM_ABI --ndk_sdkver=$TARGET_PLATFORM_VERS --toolchain=ndk --ndk=$ANDROID_NDK_HOME --buildir=$PACKAGE_BCACHED_DIR --cc=$CC --cxx=$CXX --cflags='$CFLAGS' --cxxflags='$CXXFLAGS' --ldflags='$LDFLAGS' --shflags='$LDFLAGS'"

        run $XMAKE config $XMAKE_CONFIG_OPTIONS &&
        run $XMAKE --jobs=$BUILD_NJOBS &&
        run $XMAKE install -o "$PACKAGE_INSTALL_DIR"
    fi
}

# https://mesonbuild.com/Cross-compilation.html
# run in a subshell
mesonw() {
    case $TARGET_PLATFORM_ARCH in
        armv7a)
            HOST_MACHINE_CPU_FAMILY='arm'
            HOST_MACHINE_CPU_NAME="$TARGET_PLATFORM_ARCH"
            ;;
        aarch64)
            HOST_MACHINE_CPU_FAMILY='aarch64'
            HOST_MACHINE_CPU_NAME='armv8a'
            ;;
        i686)
            HOST_MACHINE_CPU_FAMILY='x86'
            HOST_MACHINE_CPU_NAME="$TARGET_PLATFORM_ARCH"
            ;;
        x86_64)
            HOST_MACHINE_CPU_FAMILY='x86_64'
            HOST_MACHINE_CPU_NAME="$TARGET_PLATFORM_ARCH"
            ;;
    esac

    MESON_CROSS_FILE="$PACKAGE_BCACHED_DIR/cross-file"

    cat > "$MESON_CROSS_FILE" <<EOF
[host_machine]
system = 'android'
endian = 'little'
cpu_family = '$HOST_MACHINE_CPU_FAMILY'
cpu = '$HOST_MACHINE_CPU_NAME'

[binaries]
c = '$CC'
cpp = '$CXX'
ar = '$AR'
strip = '$STRIP'
cmake = '$CMAKE'
pkg-config = '$PKG_CONFIG'

[built-in options]
c_args = $(to_meson_array $CFLAGS $CPPFLAGS)
c_link_args = $(to_meson_array $LDFLAGS)
cpp_args = $(to_meson_array $CXXFLAGS $CPPFLAGS)
cpp_link_args = $(to_meson_array $LDFLAGS)
EOF

    MESON_SETUP_ARGS="--prefix=$PACKAGE_INSTALL_DIR --buildtype=$PROFILE --backend=ninja --pkg-config-path=$PKG_CONFIG_PATH --build.pkg-config-path=$PKG_CONFIG_PATH_FOR_BUILD --cross-file=$MESON_CROSS_FILE -Dlibdir=lib"

    if [ "$PACKAGE_PKGTYPE" = exe ] || [ "$PACKAGE_PKGTYPE" = pie ] ; then
        MESON_SETUP_ARGS="$MESON_SETUP_ARGS -Ddefault_library=static --prefer-static"
    else
        MESON_SETUP_ARGS="$MESON_SETUP_ARGS -Ddefault_library=both"
    fi

    MESON_COMPILE_ARGS="-C $PACKAGE_BCACHED_DIR -j $BUILD_NJOBS"
    MESON_INSTALL_ARGS="-C $PACKAGE_BCACHED_DIR"

    if [ "$VERBOSE_MESON" = 1 ] ; then
        MESON_COMPILE_ARGS="$MESON_COMPILE_ARGS -v"
    fi

    run "$MESON" setup   "$MESON_SETUP_ARGS" "$@" "$PACKAGE_BCACHED_DIR" "$PACKAGE_BSCRIPT_DIR" &&
    run "$MESON" compile "$MESON_COMPILE_ARGS" &&
    run "$MESON" install "$MESON_INSTALL_ARGS"
}

to_meson_array() {
    RESULT=

    for item in "$@"
    do
        if [ -z "$RESULT" ] ; then
            RESULT="'$item'"
        else
            RESULT="$RESULT, '$item'"
        fi
    done

    printf '[%s]\n' "$RESULT"
}

# https://developer.android.com/ndk/guides/ndk-build
ndk_build() {
    # https://developer.android.com/ndk/downloads/revision_history#expandable-33
    # Android NDK r7 (November 2011)
    if [ "$ENABLE_CCACHE" = 1 ] ; then
        export NDK_CCACHE=$CCACHE
    fi

    NDK_BUILD_ARGS="NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk APP_PLATFORM=android-$TARGET_PLATFORM_VERS APP_STRIP_MODE=-S"

    case $PROFILE in
        debug)   NDK_BUILD_ARGS="$NDK_BUILD_ARGS NDK_DEBUG=1" ;;
        release) NDK_BUILD_ARGS="$NDK_BUILD_ARGS NDK_DEBUG=0" ;;
    esac

    if [ "$REQUEST_TO_EXPORT_COMPILE_COMMANDS_JSON" = 1 ] ; then
        NDK_BUILD_ARGS="$NDK_BUILD_ARGS GEN_COMPILE_COMMANDS_DB=true"
    fi

    if [ "$LOG_LEVEL" -ge "$LOG_LEVEL_VERBOSE" ] ; then
        NDK_BUILD_ARGS="$NDK_BUILD_ARGS V=1"
    fi

    if [ "$LOG_LEVEL" -ge "$LOG_LEVEL_VERY_VERBOSE" ] ; then
        NDK_BUILD_ARGS="$NDK_BUILD_ARGS NDK_LOG=1"
    fi

    run ndk-build $NDK_BUILD_ARGS
}

########################################################################

inspect_android_ndk_info() {
    [ -z "$1" ] && abort 1 "$NDKPKG_ARG0 ndkinfo <ANDROID-NDK-HOME> , <ANDROID-NDK-HOME> is not given."
    [ -d "$1" ] || abort 1 "'$1' was expected to be a directory, but it was not."

    NDK_SOURCE_PROPERTIES_FILEPATH="$1/source.properties"

    [ -e "$NDK_SOURCE_PROPERTIES_FILEPATH" ] || abort 1 "'$NDK_SOURCE_PROPERTIES_FILEPATH' was expected to be exist, but it was not."
    [ -f "$NDK_SOURCE_PROPERTIES_FILEPATH" ] || abort 1 "'$NDK_SOURCE_PROPERTIES_FILEPATH' was expected to be a regular file, but it was not."

    ANDROID_CMAKE_TOOLCHAIN_FILE="$1/build/cmake/android.toolchain.cmake"

    [ -e "$ANDROID_CMAKE_TOOLCHAIN_FILE" ] || abort 1 "'$ANDROID_CMAKE_TOOLCHAIN_FILE' was expected to be exist, but it was not."
    [ -f "$ANDROID_CMAKE_TOOLCHAIN_FILE" ] || abort 1 "'$ANDROID_CMAKE_TOOLCHAIN_FILE' was expected to be a regular file, but it was not."

    ANDROID_NDK_VERSION=
    ANDROID_NDK_VERSION_MAJOR=

    ANDROID_NDK_VERSION="$(sed -n '/Pkg.Revision/p' "$NDK_SOURCE_PROPERTIES_FILEPATH" | sed  's/Pkg\.Revision = \(.*\).*/\1/')"

    if [ -z "$ANDROID_NDK_VERSION" ] ; then
        abort 1 "no Pkg.Revision property in $NDK_SOURCE_PROPERTIES_FILEPATH"
    fi

    ANDROID_NDK_VERSION_MAJOR="$(printf '%s\n' "$ANDROID_NDK_VERSION" | cut -d. -f1)"

    if [ "$NATIVE_PLATFORM_KIND" = darwin ] ; then
        ANDROID_NDK_TOOLCHAIN_HOST_TAG='darwin-x86_64'
    else
        ANDROID_NDK_TOOLCHAIN_HOST_TAG="linux-$NATIVE_PLATFORM_ARCH"
    fi

    ANDROID_NDK_TOOLCHAIN_ROOT="$1/toolchains/llvm/prebuilt/$ANDROID_NDK_TOOLCHAIN_HOST_TAG"

    [ -e "$ANDROID_NDK_TOOLCHAIN_ROOT" ] || abort 1 "'$ANDROID_NDK_TOOLCHAIN_ROOT' was expected to be exist, but it was not."
    [ -d "$ANDROID_NDK_TOOLCHAIN_ROOT" ] || abort 1 "'$ANDROID_NDK_TOOLCHAIN_ROOT' was expected to be a directory, but it was not."

    ANDROID_NDK_TOOLCHAIN_BIND="$ANDROID_NDK_TOOLCHAIN_ROOT/bin"

    [ -e "$ANDROID_NDK_TOOLCHAIN_BIND" ] || abort 1 "'$ANDROID_NDK_TOOLCHAIN_BIND' was expected to be exist, but it was not."
    [ -d "$ANDROID_NDK_TOOLCHAIN_BIND" ] || abort 1 "'$ANDROID_NDK_TOOLCHAIN_BIND' was expected to be a directory, but it was not."

    ANDROID_NDK_SYSROOT="$ANDROID_NDK_TOOLCHAIN_ROOT/sysroot"

    [ -e "$ANDROID_NDK_SYSROOT" ] || abort 1 "'$ANDROID_NDK_SYSROOT' was expected to be exist, but it was not."
    [ -d "$ANDROID_NDK_SYSROOT" ] || abort 1 "'$ANDROID_NDK_SYSROOT' was expected to be a directory, but it was not."

    #########################################################################################

    ANDROID_NDK_CC="$ANDROID_NDK_TOOLCHAIN_BIND/clang"
    ANDROID_NDK_CXX="$ANDROID_NDK_TOOLCHAIN_BIND/clang++"
    ANDROID_NDK_CPP="$ANDROID_NDK_CC -E"

    # https://github.com/android/ndk/wiki/Changelog-r22
    # https://github.com/android/ndk/wiki/Changelog-r23
    if [ "$ANDROID_NDK_VERSION_MAJOR" -ge 22 ] ; then
        ANDROID_NDK_LD="$ANDROID_NDK_TOOLCHAIN_BIND/ld.lld"
        ANDROID_NDK_AS="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-as"
        ANDROID_NDK_AR="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-ar"
        ANDROID_NDK_NM="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-nm"
        ANDROID_NDK_SIZE="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-size"
        ANDROID_NDK_STRIP="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-strip"
        ANDROID_NDK_RANLIB="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-ranlib"
        ANDROID_NDK_STRINGS="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-strings"
        ANDROID_NDK_OBJDUMP="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-objdump"
        ANDROID_NDK_OBJCOPY="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-objcopy"
        ANDROID_NDK_READELF="$ANDROID_NDK_TOOLCHAIN_BIND/llvm-readelf"
    else
        ANDROID_NDK_LD="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-ld"
        ANDROID_NDK_AS="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-as"
        ANDROID_NDK_AR="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-ar"
        ANDROID_NDK_NM="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-nm"
        ANDROID_NDK_SIZE="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-size"
        ANDROID_NDK_STRIP="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-strip"
        ANDROID_NDK_RANLIB="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-ranlib"
        ANDROID_NDK_STRINGS="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-strings"
        ANDROID_NDK_OBJDUMP="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-objdump"
        ANDROID_NDK_OBJCOPY="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-objcopy"
        ANDROID_NDK_READELF="$ANDROID_NDK_TOOLCHAIN_BIND/aarch64-linux-android-readelf"
    fi

    #########################################################################################

    ANDROID_NDK_SUPPORTED_SDK_API_LEVEL_ALL="$(ls "$ANDROID_NDK_TOOLCHAIN_BIND" | sed -n '/aarch64-linux-android[1-9][0-9]-clang++/p' | cut -d- -f3 | sed 's/android//')"
    ANDROID_NDK_SUPPORTED_MIN_SDK_API_LEVEL="$(printf '%s\n' "$ANDROID_NDK_SUPPORTED_SDK_API_LEVEL_ALL" | sort | head -n 1)"
    ANDROID_NDK_SUPPORTED_MAX_SDK_API_LEVEL="$(printf '%s\n' "$ANDROID_NDK_SUPPORTED_SDK_API_LEVEL_ALL" | sort | tail -n 1)"

    #########################################################################################

    ANDROID_NDK_HOME="$1"
    ANDROID_NDK_ROOT="$1"
}

println_android_ndk_info() {
    cat <<EOF
ANDROID_NDK_HOME='$ANDROID_NDK_HOME'
ANDROID_NDK_ROOT='$ANDROID_NDK_ROOT'
ANDROID_NDK_VERSION='$ANDROID_NDK_VERSION'
ANDROID_NDK_VERSION_MAJOR='$ANDROID_NDK_VERSION_MAJOR'
ANDROID_NDK_TOOLCHAIN_ROOT='$ANDROID_NDK_TOOLCHAIN_ROOT'
ANDROID_NDK_TOOLCHAIN_BIND='$ANDROID_NDK_TOOLCHAIN_BIND'
ANDROID_NDK_SYSROOT='$ANDROID_NDK_SYSROOT'
ANDROID_NDK_CC='$ANDROID_NDK_CC'
ANDROID_NDK_CXX='$ANDROID_NDK_CXX'
ANDROID_NDK_CPP='$ANDROID_NDK_CPP'
ANDROID_NDK_LD='$ANDROID_NDK_LD'
ANDROID_NDK_AS='$ANDROID_NDK_AS'
ANDROID_NDK_AR='$ANDROID_NDK_AR'
ANDROID_NDK_NM='$ANDROID_NDK_NM'
ANDROID_NDK_SIZE='$ANDROID_NDK_SIZE'
ANDROID_NDK_STRIP='$ANDROID_NDK_STRIP'
ANDROID_NDK_RANLIB='$ANDROID_NDK_RANLIB'
ANDROID_NDK_STRINGS='$ANDROID_NDK_STRINGS'
ANDROID_NDK_OBJDUMP='$ANDROID_NDK_OBJDUMP'
ANDROID_NDK_OBJCOPY='$ANDROID_NDK_OBJCOPY'
ANDROID_NDK_READELF='$ANDROID_NDK_READELF'
ANDROID_NDK_SUPPORTED_MIN_SDK_API_LEVEL='$ANDROID_NDK_SUPPORTED_MIN_SDK_API_LEVEL'
ANDROID_NDK_SUPPORTED_MAX_SDK_API_LEVEL='$ANDROID_NDK_SUPPORTED_MAX_SDK_API_LEVEL'
EOF
}

# setup_android_ndk_env [ANDROID_NDK_HOME]
  setup_android_ndk_env() {
    if [ -z "$1" ] ; then
        if [ "$NATIVE_PLATFORM_KIND" = android ] ; then
            # use comunity compiled Android NDK for aarch64-linux right now, I might compile my own at some point.
            # https://github.com/lzhiyong/termux-ndk/releases/tag/android-ndk
            ANDROID_NDK_REVISION='r26b'
        else
            ANDROID_NDK_REVISION='r27c'
        fi

        ANDROID_NDK_PKG="android-ndk-$ANDROID_NDK_REVISION"

        ANDROID_NDK_HOME="$UPPM_HOME/installed/$ANDROID_NDK_PKG"

        if ! uppm is-installed "$ANDROID_NDK_PKG" ; then
            note "Due to --ndk-home=<ANDROID-NDK-HOME> option is unspecified, Android NDK ($ANDROID_NDK_REVISION) will be installed via uppm."

            uppm about
            uppm update
            uppm install "$ANDROID_NDK_PKG"

            case $NATIVE_PLATFORM_KIND in
                darwin)
                    # https://github.com/android/ndk/issues/1549
                    # remove the yasm shipped with Android NDK and use our own
                    rm -rf "$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/yasm"
                    ;;
            esac
        fi
    else
        ANDROID_NDK_HOME="$1"
    fi

    inspect_android_ndk_info "$ANDROID_NDK_HOME"

    if [ "$DUMP_NDK" = 1 ] ; then
        println_android_ndk_info
    fi

    export ANDROID_NDK_HOME
    export ANDROID_NDK_ROOT
}

# }}}
##############################################################################
# {{{ install_g++_via_system_package_manager

__install_gcc_via_syspm_debian() {
    $sudo apt-get -y update
    $sudo apt-get -y install g++
}

__install_gcc_via_syspm_ubuntu() {
    $sudo apt-get -y update
    $sudo apt-get -y install g++
}

__install_gcc_via_syspm_linuxmint() {
    $sudo apt-get -y update
    $sudo apt-get -y install g++
}

__install_gcc_via_syspm_rocky() {
    $sudo dnf -y update
    $sudo dnf -y install gcc-c++
}

__install_gcc_via_syspm_centos() {
    $sudo dnf -y update
    $sudo dnf -y install gcc-c++
}

__install_gcc_via_syspm_fedora() {
    $sudo dnf -y update
    $sudo dnf -y install curl g++
}

__install_gcc_via_syspm_rhel() {
:
}

__install_gcc_via_syspm_opensuse_leap() {
    $sudo zypper update  -y
    $sudo zypper install -y gcc-c++
}

__install_gcc_via_syspm_gentoo() {
    $sudo emerge sys-devel/gcc
}

__install_gcc_via_syspm_manjaro() {
    $sudo pacman -Syyuu --noconfirm
    $sudo pacman -S     --noconfirm gcc
}

__install_gcc_via_syspm_arch() {
    $sudo pacman -Syyuu --noconfirm
    $sudo pacman -S     --noconfirm gcc
}

__install_gcc_via_syspm_void() {
    $sudo xbps-install -Syu xbps
    $sudo xbps-install -S
    $sudo xbps-install -Syu gcc
}

__install_gcc_via_syspm_alpine() {
    $sudo apk update
    $sudo apk add gcompat g++ linux-headers
}

__install_gcc_via_syspm() {
    if [ -f /etc/os-release ] ; then
        .   /etc/os-release

        if [ "$ID" = 'opensuse-leap' ] ; then
            ID='opensuse_leap'
        fi

        __install_gcc_via_syspm_$ID
    fi
}

# }}}
##############################################################################
# {{{ ndk-pkg setup

__setup() {
    unset REQUEST_TO_INSTALL_CURL_VIA_SYSTEM_PACKAGE_MANAGER

    if [ "$1" = -y ] ; then
        REQUEST_TO_INSTALL_CURL_VIA_SYSTEM_PACKAGE_MANAGER=1
    fi

    unset NATIVE_PLATFORM_ARCH
    unset NATIVE_PLATFORM_VERS

    unset LINUX

    case "$(uname -s)" in
        Darwin)
            NATIVE_PLATFORM_VERS_MAJOR="$(sw_vers -productVersion | cut -d. -f1)"

            if [ "$NATIVE_PLATFORM_VERS_MAJOR" -le 10 ] ; then
                NATIVE_PLATFORM_VERS='10.15'
            elif [ "$NATIVE_PLATFORM_VERS_MAJOR" -ge 15 ] ; then
                NATIVE_PLATFORM_VERS='15.0'
            else
                NATIVE_PLATFORM_VERS="$NATIVE_PLATFORM_VERS_MAJOR.0"
            fi

            NATIVE_PLATFORM_ARCH="$(uname -m)"
            NATIVE_PLATFORM_TRIPLE="macos-$NATIVE_PLATFORM_VERS-$NATIVE_PLATFORM_ARCH"
            ;;
        Linux)
            if [ "$(uname -o 2>/dev/null || true)" = Android ] ; then
                NATIVE_PLATFORM_VERS="$(getprop ro.build.version.sdk)"
                NATIVE_PLATFORM_TRIPLE="android-$NATIVE_PLATFORM_VERS-arm64-v8a"
            else
                NATIVE_PLATFORM_ARCH="$(uname -m)"
                NATIVE_PLATFORM_TRIPLE="linux-$NATIVE_PLATFORM_ARCH"

                LINUX=1

                if [ -f /etc/os-release ] ; then
                    .   /etc/os-release

                    unset sudo

                    if [ "$(id -u)" -ne 0 ] ; then
                        sudo=sudo
                    fi

                    # https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
                    if [ "$GITHUB_ACTIONS" = true ] ; then
                        if [ -f '/lib/ld-musl-x86_64.so.1' ] ; then
                            if [ ! -f '/lib64/ld-linux-x86-64.so.2' ] ; then
                                case $ID in
                                    alpine)
                                        run $sudo apk update
                                        run $sudo apk add gcompat
                                        ;;
                                esac
                            fi
                        fi
                    fi

                    for FETCH_TOOL in curl wget http lynx aria2c axel
                    do
                        if command -v "$FETCH_TOOL" > /dev/null ; then
                            break
                        else
                            unset FETCH_TOOL
                        fi
                    done

                    if [ -z "$FETCH_TOOL" ] ; then
                        if [ "$REQUEST_TO_INSTALL_CURL_VIA_SYSTEM_PACKAGE_MANAGER" = 1 ] ; then
                            FETCH_TOOL=curl

                            case $ID in
                                debian|ubuntu|linuxmint)
                                    run $sudo apt-get -y update
                                    run $sudo apt-get -y install curl
                                    ;;
                                fedora|centos|rocky)
                                    run $sudo dnf -y update
                                    run $sudo dnf -y install curl
                                    ;;
                                opensuse-leap)
                                    run $sudo zypper update  -y
                                    run $sudo zypper install -y curl
                                    ;;
                                gentoo)
                                    run $sudo emerge net-misc/curl
                                    ;;
                                arch|manjaro)
                                    run $sudo pacman -Syyuu --noconfirm
                                    run $sudo pacman -S     --noconfirm curl
                                    ;;
                                void)
                                    run $sudo xbps-install -Syu xbps
                                    run $sudo xbps-install -S
                                    run $sudo xbps-install -Syu curl
                                    ;;
                                alpine)
                                    run $sudo apk update
                                    run $sudo apk add curl
                                    ;;
                                *)  unset FETCH_TOOL
                            esac
                        fi

                        if [ -z "$FETCH_TOOL" ] ; then
                            abort 1 "none of curl wget http lynx aria2c axel command was found, please install one of them then try again."
                        fi
                    fi
                fi
            fi
    esac

    ##################################################################################

    SESSION_DIR="$NDKPKG_HOME/run/$$/core"

    run rm -rf     "$SESSION_DIR"
    run install -d "$SESSION_DIR"
    run cd         "$SESSION_DIR"

    ##################################################################################

    BSDTAR="$(command -v bsdtar || true)"

    if [ -n "$BSDTAR" ] ; then
        TAR="$BSDTAR"
    else
        TAR="$(command -v gtar || command -v tar || true)"
        XZ="$(command -v xz || true)"

        if [ -z "$TAR" ] || [ -z "$XZ" ] ; then
            wfetch 'https://github.com/leleliu008/bsdtar.exe/releases/download/2024.10.03/bsdtar-3.7.4-linux-x86_64.exe' -o bsdtar --no-buffer
            chmod +x bsdtar
            TAR=./bsdtar
        fi
    fi

    ##################################################################################

    # https://curl.se/docs/caextract.html
    wfetch 'https://curl.se/ca/cacert.pem' --no-buffer

    export SSL_CERT_FILE="$PWD/cacert.pem"

    ##################################################################################

    wfetch 'https://raw.githubusercontent.com/leleliu008/ndk-pkg/master/ndk-pkg-core-latest-release-version' --no-buffer

    NDKPKG_CORE_LATEST_RELEASE_VERSION="$(cat ndk-pkg-core-latest-release-version)"
    NDKPKG_CORE_LATEST_RELEASE_TAG="ndk-pkg-core-$NDKPKG_CORE_LATEST_RELEASE_VERSION"
    NDKPKG_CORE_LATEST_RELEASE_TARBALL_FILENAME="$NDKPKG_CORE_LATEST_RELEASE_TAG-$NATIVE_PLATFORM_TRIPLE.tar.xz"
    NDKPKG_CORE_LATEST_RELEASE_TARBALL_URL="https://github.com/leleliu008/ndk-pkg/releases/download/$NDKPKG_CORE_LATEST_RELEASE_TAG/$NDKPKG_CORE_LATEST_RELEASE_TARBALL_FILENAME"

    wfetch "$NDKPKG_CORE_LATEST_RELEASE_TARBALL_URL" --no-buffer

    run $TAR xvf "$NDKPKG_CORE_LATEST_RELEASE_TARBALL_FILENAME" --strip-components=1 --no-same-owner

    ##################################################################################

    run ./uppm about
    run ./uppm update

    unset UPPM_PACKAGE_EXEFIND_PATH
    unset UPPM_PACKAGE_ACLOCAL_PATH

    for UPPM_PACKAGE_NAME in bash coreutils findutils gawk gsed grep bsdtar zip git curl tree sysinfo patchelf xxd fzf bat jq yq d2 dot_static
    do
        run ./uppm install "$UPPM_PACKAGE_NAME"

        UPPM_PACKAGE_INSTALLED_DIR="$UPPM_HOME/installed/$UPPM_PACKAGE_NAME"

        if [ -d "$UPPM_PACKAGE_INSTALLED_DIR/bin" ] ; then
            UPPM_PACKAGE_EXEFIND_PATH="$UPPM_PACKAGE_EXEFIND_PATH:$UPPM_PACKAGE_INSTALLED_DIR/bin"
        fi

        if [ -d "$UPPM_PACKAGE_INSTALLED_DIR/sbin" ] ; then
            UPPM_PACKAGE_EXEFIND_PATH="$UPPM_PACKAGE_EXEFIND_PATH:$UPPM_PACKAGE_INSTALLED_DIR/sbin"
        fi

        if [ -d "$UPPM_PACKAGE_INSTALLED_DIR/share/aclocal" ] ; then
            UPPM_PACKAGE_ACLOCAL_PATH="$UPPM_PACKAGE_ACLOCAL_PATH:$UPPM_PACKAGE_INSTALLED_DIR/share/aclocal"
        fi
    done

    UPPM_PACKAGE_EXEFIND_PATH="${UPPM_PACKAGE_EXEFIND_PATH#':'}"
    UPPM_PACKAGE_ACLOCAL_PATH="${UPPM_PACKAGE_ACLOCAL_PATH#':'}"

    ##################################################################################

    cat > init.sh <<EOF
export ACLOCAL_PATH="$UPPM_PACKAGE_ACLOCAL_PATH:\$ACLOCAL_PATH"
export PATH="$UPPM_PACKAGE_EXEFIND_PATH:\$PATH"

# https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables
if [ -d "\$UPPM_HOME/installed/git/libexec/git-core" ] ; then
    export GIT_EXEC_PATH="\$UPPM_HOME/installed/git/libexec/git-core"
    export GIT_TEMPLATE_DIR="\$UPPM_HOME/installed/git/share/git-core/templates"
    export GIT_CONFIG_NOSYSTEM=1
fi
EOF

    ##################################################################################

    run "$UPPM_HOME/installed/gsed/bin/gsed" -i "'s|NDKPKG_CORE_DIR|$NDKPKG_CORE_DIR|'" fonts.conf

    ##################################################################################

    if [ "$LINUX" = 1 ] ; then
        if [ -f '/lib/ld-musl-x86_64.so.1' ] ; then
            cat >> init.sh <<EOF
export LD_LIBRARY_PATH="\$NDKPKG_HOME/core/lib"
EOF
            wfetch 'https://musl.cc/x86_64-linux-musl-native.tgz' --no-buffer
            run $TAR xvf x86_64-linux-musl-native.tgz --strip-components=1 --no-same-owner
        else
        wfetch 'https://github.com/leleliu008/test/releases/download/2023.10.08/gcc-13.1.0-linux-glibc-x86_64.tar.xz' --no-buffer
        run $TAR xf gcc-13.1.0-linux-glibc-x86_64.tar.xz --strip-components=1

        wfetch 'https://github.com/leleliu008/test/releases/download/2023.10.08/binutils-2.41-linux-glibc-x86_64.tar.xz' --no-buffer
        run $TAR xf binutils-2.41-linux-glibc-x86_64.tar.xz --strip-components=1

        run install -d sysroot
        wfetch 'https://github.com/leleliu008/test/releases/download/2023.10.08/glibc-2.40-linux-glibc-x86_64.tar.xz' --no-buffer
        run $TAR xf glibc-2.40-linux-glibc-x86_64.tar.xz -C sysroot --strip-components=1

        wfetch 'https://github.com/leleliu008/test/releases/download/2023.10.08/linux-headers-6.8-linux-x86_64.tar.xz' --no-buffer
        run $TAR xf linux-headers-6.8-linux-x86_64.tar.xz -C sysroot --strip-components=1

        ln -s gcc/x86_64-pc-linux-gnu/13.1.0/libgcc.a lib/libgcc.a

        "$UPPM_HOME/installed/gsed/bin/gsed" -i "s|/home/leleliu008/.ppkg/installed/linux-glibc-x86_64/bebfceb3d2ea2461fde461b6c32a3e3cafc29d18ecec16e136e32b175caab53d|$NDKPKG_HOME/core/sysroot|g" sysroot/lib/libc.so sysroot/lib/libm.a sysroot/lib/libm.so

        mv bin/gcc bin/gcc.exe
        mv bin/g++ bin/g++.exe

        cat > bin/gcc <<EOF
#!/bin/sh
exec "$NDKPKG_HOME/core/bin/gcc.exe" "\$@" -specs "$NDKPKG_HOME/core/gcc.specs"
EOF
        cat > bin/g++ <<EOF
#!/bin/sh
exec "$NDKPKG_HOME/core/bin/g++.exe" "\$@" -specs "$NDKPKG_HOME/core/g++.specs"
EOF
        chmod +x bin/gcc
        chmod +x bin/g++

        # https://gcc.gnu.org/onlinedocs/gcc/Spec-Files.html
        cat > gcc.specs <<EOF
%rename cpp_options old_cpp_options

*cpp_options:
-nostdinc -isystem $NDKPKG_HOME/core/sysroot/include -isystem include%s %(old_cpp_options)

*cc1:
%(cc1_cpu) -nostdinc -isystem $NDKPKG_HOME/core/sysroot/include -isystem include%s

*link_libgcc:
-L$NDKPKG_HOME/core/sysroot/lib -L .%s

*libgcc:
libgcc.a%s %:if-exists(libgcc_eh.a%s)

*startfile:
%{!shared: $NDKPKG_HOME/core/sysroot/lib/Scrt1.o} $NDKPKG_HOME/core/sysroot/lib/crti.o crtbeginS.o%s

*endfile:
crtendS.o%s $NDKPKG_HOME/core/sysroot/lib/crtn.o

*link:
-dynamic-linker $NDKPKG_HOME/core/sysroot/lib/ld-linux-x86-64.so.2 -rpath $NDKPKG_HOME/core/sysroot/lib -rpath $NDKPKG_HOME/core/lib -L $NDKPKG_HOME/core/sysroot/lib -L $NDKPKG_HOME/core/lib -nostdlib %{shared:-shared} %{static:-static} %{rdynamic:-export-dynamic}

*esp_link:


*esp_options:


*esp_cpp_options:


EOF

        cat > g++.specs <<EOF
%rename cpp_options old_cpp_options

*cpp_options:
-nostdinc -isystem $NDKPKG_HOME/core/sysroot/include -I $NDKPKG_HOME/core/include/c++/13.1.0 -I $NDKPKG_HOME/core/include/c++/13.1.0/x86_64-pc-linux-gnu -isystem include%s %(old_cpp_options)

*cc1:
%(cc1_cpu) -nostdinc -isystem $NDKPKG_HOME/core/sysroot/include -I $NDKPKG_HOME/core/include/c++/13.1.0 -I $NDKPKG_HOME/core/include/c++/13.1.0/x86_64-pc-linux-gnu -isystem include%s

*link_libgcc:
-L$NDKPKG_HOME/core/sysroot/lib -L .%s

*libgcc:
libgcc.a%s %:if-exists(libgcc_eh.a%s)

*startfile:
%{!shared: $NDKPKG_HOME/core/sysroot/lib/Scrt1.o} $NDKPKG_HOME/core/sysroot/lib/crti.o crtbeginS.o%s

*endfile:
crtendS.o%s $NDKPKG_HOME/core/sysroot/lib/crtn.o

*link:
-dynamic-linker $NDKPKG_HOME/core/sysroot/lib/ld-linux-x86-64.so.2 -rpath $NDKPKG_HOME/core/sysroot/lib -rpath $NDKPKG_HOME/core/lib -L $NDKPKG_HOME/core/sysroot/lib -L $NDKPKG_HOME/core/lib -nostdlib %{shared:-shared} %{static:-static} %{rdynamic:-export-dynamic}

*esp_link:


*esp_options:


*esp_cpp_options:


EOF

        fi

        ln -s ../wrapper-native-cc bin/cc
    fi

    ##################################################################################

    if [ -d        "$NDKPKG_CORE_DIR" ] ; then
        run rm -rf "$NDKPKG_CORE_DIR"
    fi

    run mv "$SESSION_DIR" "$NDKPKG_HOME/"

    #################################################################################

    success "ndk-pkg-$NDKPKG_VERSION have been successfully setup."
}

__build_linux_headers() {
    export PATH="$PWD/bin:$PATH"

    LINUX_KERNAL_VERSION="$(cut -d ' ' -f3 < /proc/version | cut -d- -f1)"
    LINUX_KERNAL_VERSION="${LINUX_KERNAL_VERSION%.0}"
    LINUX_KERNAL_VERSION_MAJOR="${LINUX_KERNAL_VERSION%%.*}"
    LINUX_KERNAL_SRC_FILENAME="linux-$LINUX_KERNAL_VERSION.tar.xz"

    wfetch "https://cdn.kernel.org/pub/linux/kernel/v$LINUX_KERNAL_VERSION_MAJOR.x/$LINUX_KERNAL_SRC_FILENAME" --no-buffer

    run install -d linux-src
    run $TAR xf "$LINUX_KERNAL_SRC_FILENAME" -C linux-src --strip-components=1
    run cd linux-src
    run "$UPPM_HOME/installed/gmake/bin/gmake" headers_install INSTALL_HDR_PATH=../sysroot
    cd -
}

# }}}
##############################################################################
# {{{ ndk-pkg help

__help() {
    cat <<EOF
           _ _               _         
 _ __   __| | | __     _ __ | | ____ _ 
| '_ \ / _\` | |/ /____| '_ \| |/ / _\` |
| | | | (_| |   <_____| |_) |   < (_| |
|_| |_|\__,_|_|\_\    | .__/|_|\_\__, |
                      |_|        |___/  $NDKPKG_VERSION

EOF

    printf '%b\n' "
${COLOR_GREEN}A package builder/manager for Android NDK to build projects written in C, C++, Rust, Golang, etc.${COLOR_OFF}

${COLOR_GREEN}ndk-pkg <ACTION> [ARGUMENT...]${COLOR_OFF}

${COLOR_GREEN}ndk-pkg --help${COLOR_OFF}
${COLOR_GREEN}ndk-pkg -h${COLOR_OFF}
    show help of this command.

${COLOR_GREEN}ndk-pkg --version${COLOR_OFF}
${COLOR_GREEN}ndk-pkg -V${COLOR_OFF}
    show version of this command.

${COLOR_GREEN}ndk-pkg setup [-y]${COLOR_OFF}
    install essential tools (e.g. uppm, bash, coreutils, findutils, gsed, gawk, grep, tree, git, curl, bsdtar, zip, xxd, fzf, bat, jq, yq, d2, sysinfo, pkg-config, patchelf, etc) used by this shell script.

    If -y is given, install curl via your system's package manager if none of curl wget http lynx aria2c axel command is found.

${COLOR_GREEN}ndk-pkg about${COLOR_OFF}
    show basic information about this software.

${COLOR_GREEN}ndk-pkg sysinfo${COLOR_OFF}
    show basic information about your current running operation system.

${COLOR_GREEN}ndk-pkg ndkinfo <ANDROID-NDK-HOME>${COLOR_OFF}
    show basic information about the specified location of Android NDK.

${COLOR_GREEN}ndk-pkg gen-url-transform-sample${COLOR_OFF}
    generate url-transform sample.

${COLOR_GREEN}ndk-pkg integrate zsh [--output-dir=<DIR>]${COLOR_OFF}
    download a zsh-completion script file to a approprivate location.

${COLOR_GREEN}ndk-pkg update${COLOR_OFF}
    update all the available formula repositories.

${COLOR_GREEN}ndk-pkg upgrade-self${COLOR_OFF}
    upgrade this software.

${COLOR_GREEN}ndk-pkg cleanup${COLOR_OFF}
    delete the unused cached files.


${COLOR_GREEN}ndk-pkg formula-repo-add  <FORMULA-REPO-NAME> <FORMULA-REPO-URL> [--branch=VALUE --pin/--unpin --enable/--disable --sync]${COLOR_OFF}
    create a new empty formula repository. If --sync option is given, sync with the server after the formula repo is created.

${COLOR_GREEN}ndk-pkg formula-repo-del  <FORMULA-REPO-NAME>${COLOR_OFF}
    delete the given formula repository.

${COLOR_GREEN}ndk-pkg formula-repo-sync <FORMULA-REPO-NAME>${COLOR_OFF}
    update the given formula repository.

${COLOR_GREEN}ndk-pkg formula-repo-info <FORMULA-REPO-NAME>${COLOR_OFF}
    show information of the given formula repository.

${COLOR_GREEN}ndk-pkg formula-repo-conf <FORMULA-REPO-NAME> [--url=VALUE --branch=VALUE --pin/--unpin --enable/--disable]${COLOR_OFF}
    change the config of the given formula repository.

${COLOR_GREEN}ndk-pkg formula-repo-list${COLOR_OFF}
    list all available formula repositories.


${COLOR_GREEN}ndk-pkg info-available <PACKAGE-NAME> [--json | --yaml | <KEY>]${COLOR_OFF}
    show information of the given available package.

${COLOR_GREEN}ndk-pkg info-installed <PACKAGE-SPEC> [--json | --yaml | <KEY>]${COLOR_OFF}
    show information of the given installed package.


${COLOR_GREEN}ndk-pkg ls-available [-v] [--json | --yaml]${COLOR_OFF}
    list all the available packages.

${COLOR_GREEN}ndk-pkg ls-installed${COLOR_OFF}
    list all the installed packages.

${COLOR_GREEN}ndk-pkg ls-outdated${COLOR_OFF}
    list all the outdated  packages.


${COLOR_GREEN}ndk-pkg is-available <PACKAGE-NAME>${COLOR_OFF}
    check if the given package is available.

${COLOR_GREEN}ndk-pkg is-installed <PACKAGE-SPEC>${COLOR_OFF}
    check if the given package is installed.

${COLOR_GREEN}ndk-pkg is-outdated  <PACKAGE-SPEC>${COLOR_OFF}
    check if the given package is outdated.


${COLOR_GREEN}ndk-pkg search <REGULAR-EXPRESSION-PATTERN> [-v]${COLOR_OFF}
    search all available packages whose name matches the given regular expression pattern.


${COLOR_GREEN}ndk-pkg depends <PACKAGE-NAME> [-t <OUTPUT-TYPE>] [-o <OUTPUT-PATH>]${COLOR_OFF}
    show the packages that are depended by the given package.

    <OUTPUT-TYPE> must be any one of d2 dot box svg png

    <OUTPUT-PATH> can be either the filepath or directory.

    If <OUTPUT-PATH> is . .. or ends with slash(/), then it will be treated as a directory, otherwise, it will be treated as a filepath.

    If <OUTPUT-PATH> is treated as a directory, then it will be expanded to <OUTPUT-PATH>/<PACKAGE-NAME>-dependencies.<OUTPUT-TYPE>

    If -o <OUTPUT-PATH> option is unspecified, the result will be written to stdout.

    If -t <OUTPUT-TYPE> option is unspecified, and if <OUTPUT-PATH> ends with one of .d2 .dot .box .svg .png, <OUTPUT-TYPE> will be the <OUTPUT-PATH> suffix, otherwise, <OUTPUT-TYPE> will be box.


${COLOR_GREEN}ndk-pkg fetch <PACKAGE-NAME>${COLOR_OFF}
    download all the resources of the given package to the local cache.


${COLOR_GREEN}ndk-pkg install [android-<ANDROID-API>-<ANDROID-ABIs>/]<PACKAGE-NAME>... [INSTALL-OPTIONS]${COLOR_OFF}
    install the given packages.

    ${COLOR_RED}<ANDROID-ABIs>${COLOR_OFF} is a comma-separated list, any combination of arm64-v8a, armeabi-v7a, x86_64, x86

    INSTALL-OPTIONS:
        ${COLOR_BLUE}--target=android-<ANDROID-API>-<ANDROID-ABI>${COLOR_OFF}
            specify the target to be built for.

            If this option is unspecified, the environment variable ${COLOR_RED}NDKPKG_DEFAULT_TARGET${COLOR_OFF} is honored, if the environment variable NDKPKG_DEFAULT_TARGET is not set, then android-21-arm64-v8a will be used as the default.

        ${COLOR_BLUE}--ndk-home=<ANDROID-NDK-HOME>${COLOR_OFF}
            If --ndk-home=<ANDROID-NDK-HOME> is unspecified, a proper version of Android NDK will be automatically installed for you via uppm.

        ${COLOR_BLUE}--profile=<debug|release>${COLOR_OFF}
            specify the build profile.

            debug:
                  CFLAGS: -O0 -g
                CXXFLAGS: -O0 -g

            release:
                  CFLAGS: -Os
                CXXFLAGS: -Os
                CPPFLAGS: -DNDEBUG
                 LDFLAGS: -flto -Wl,-s

        ${COLOR_BLUE}--static${COLOR_OFF}
            create Fully Statically Linked Executables

            This option only affects the package whose type is exe. If this option is not given, ndk-pkg will create Mostly Statically Linked Executables

            a  fully statically linked executable is a executable that without any additional libraries to be loaded at runtime.
            a mostly statically linked executable is a executable that without any additional libraries except libc.so, libm.so, libdl.so, liblog.so, etc.

            a fully  statically linked executable is easy to distribute to all version of Android devices.
            a mostly statically linked executable is easy to distribute to one version of Android devices.

        ${COLOR_BLUE}-j <N>${COLOR_OFF}
            specify the max number of jobs you could run in parallel.

        ${COLOR_BLUE}-I <FORMULA-SEARCH-DIR>${COLOR_OFF}
            specify the formula search directory. This option can be used multiple times.

        ${COLOR_BLUE}-E${COLOR_OFF}
            export compile_commands.json

        ${COLOR_BLUE}-U${COLOR_OFF}
            upgrade packages if possible.

        ${COLOR_BLUE}-K${COLOR_OFF}
            keep the session directory even if this package is successfully installed.

        ${COLOR_BLUE}-q${COLOR_OFF}
            silent mode. no any messages will be output to terminal.

        ${COLOR_BLUE}-v${COLOR_OFF}
            verbose mode. many messages will be output to terminal.

            This option is equivalent to -v-* options all are supplied.

        ${COLOR_BLUE}-x${COLOR_OFF}
            very verbose mode. many many messages will be output to terminal.

            This option is equivalent to -v-* and -x-* options all are supplied.

        ${COLOR_BLUE}-v-env${COLOR_OFF}
            show all environment variables before starting to build.

        ${COLOR_BLUE}-v-http${COLOR_OFF}
            show http request/response.

        ${COLOR_BLUE}-v-ndk${COLOR_OFF}
            show Android NDK information.

        ${COLOR_BLUE}-v-formula${COLOR_OFF}
            show formula content.

        ${COLOR_BLUE}-v-go${COLOR_OFF}
            pass -v argument to go build command.

        ${COLOR_BLUE}-v-uppm${COLOR_OFF}
            pass -v argument to uppm command.

        ${COLOR_BLUE}-v-cargo${COLOR_OFF}
            pass -v argument to cargo command.

        ${COLOR_BLUE}-v-meson${COLOR_OFF}
            pass -v argument to meson command.

        ${COLOR_BLUE}-v-ninja${COLOR_OFF}
            pass -v argument to ninja command.

        ${COLOR_BLUE}-v-gmake${COLOR_OFF}
            pass V=1 argument to gmake command.

        ${COLOR_BLUE}-v-cmake${COLOR_OFF}
            set(CMAKE_VERBOSE_MAKEFILE ON)

        ${COLOR_BLUE}-v-xmake${COLOR_OFF}
            pass -v argument to xmake command.

        ${COLOR_BLUE}-x-sh${COLOR_OFF}
            set -x to debug current running shell.

        ${COLOR_BLUE}-x-cc${COLOR_OFF}
            pass -v argument to clang command.

        ${COLOR_BLUE}-x-ld${COLOR_OFF}
            pass -Wl,-v argument to linker.

        ${COLOR_BLUE}-x-go${COLOR_OFF}
            pass -x argument to go build command.

        ${COLOR_BLUE}-x-cargo${COLOR_OFF}
            pass -vv argument to cargo command.

        ${COLOR_BLUE}-x-gmake${COLOR_OFF}
            pass --debug argument to gmake command.

        ${COLOR_BLUE}-x-cmake${COLOR_OFF}
            set(CMAKE_FIND_DEBUG_MODE ON)

        ${COLOR_BLUE}-x-xmake${COLOR_OFF}
            pass -vD argument to xmake command.

        ${COLOR_BLUE}-x-pkg-config${COLOR_OFF}
            export PKG_CONFIG_DEBUG_SPEW=1

        ${COLOR_BLUE}--disable-ccache${COLOR_OFF}
            do not use ccache.

    USAGE-EXAMPLES:
        ndk-pkg install android-21-arm64-v8a/libz
        ndk-pkg install android-21-arm64-v8a,armeabi-v7a/libz
        ndk-pkg install android-21-arm64-v8a,armeabi-v7a,x86_64/libz
        ndk-pkg install android-21-arm64-v8a,armeabi-v7a,x86_64,x86/libz

${COLOR_GREEN}ndk-pkg reinstall android-<ANDROID-API>-<ANDROID-ABIs>/<PACKAGE-NAME>... [INSTALL-OPTIONS]${COLOR_OFF}
    reinstall the given packages.

${COLOR_GREEN}ndk-pkg upgrade   android-<ANDROID-API>-<ANDROID-ABIs>/<PACKAGE-NAME>... [INSTALL-OPTIONS]${COLOR_OFF}
    upgrade the given packages or all outdated packages.

${COLOR_GREEN}ndk-pkg uninstall <PACKAGE_SPEC>...${COLOR_OFF}
    uninstall the given packages.


${COLOR_GREEN}ndk-pkg tree <PACKAGE-SPEC> [--dirsfirst | -L N]${COLOR_OFF}
    list the installed files of the given installed package in a tree-like format.

${COLOR_GREEN}ndk-pkg logs <PACKAGE-SPEC>${COLOR_OFF}
    show logs of the given installed package.

    This will launch fzf finder. press ESC key to quit.

${COLOR_GREEN}ndk-pkg pack <PACKAGE-SPEC> [OPTIONS]${COLOR_OFF}
    pack the given installed package.

    ${COLOR_BLUE}-t <OUTPUT-TYPE>${COLOR_OFF}
        must be any one of tar.gz tar.xz tar.lz tar.bz2 zip

        If this option is unspecified, <OUTPUT-TYPE> would be assigned to .tar.xz

    ${COLOR_BLUE}-o <OUTPUT-PATH>${COLOR_OFF}
        specify where the packed archive file would be written to.

        <OUTPUT-PATH> can be either the filepath or directory.

        If <OUTPUT-PATH> is . .. ./ ../ or ends with slash(/), then it would be treated as a directory, otherwise, it would be treated as a filepath.

        If <OUTPUT-PATH> is treated as a filepath, then it must ends with one of .tar.gz .tar.xz .tar.lz .tar.bz2 .tgz .txz .tlz .tbz2 .zip , in this case, -t <OUTPUT-TYPE> option would be ignored if it also is specified.

        If <OUTPUT-PATH> is treated as a directory, then it would be expanded to <OUTPUT-PATH>/<PACKAGE-NAME>-<PACKAGE-VERSION>-<TARGET-PLATFORM-NAME>-<TARGET-PLATFORM-ARCH><OUTPUT-TYPE>

        If this option is unspecified, <OUTPUT-PATH> would be assigned to ./<PACKAGE-NAME>-<PACKAGE-VERSION>-<TARGET-PLATFORM-NAME>-<TARGET-PLATFORM-ARCH><OUTPUT-TYPE>

    ${COLOR_BLUE}--exclude <PATH>${COLOR_OFF}
        exclude file that isn't mean to packed into the archive file.

        <PATH> is relative to the installed root directory.

        this option can be used multiple times.

    ${COLOR_BLUE}-K${COLOR_OFF}
        keep the session directory even if this package is successfully packed.

${COLOR_GREEN}ndk-pkg export android-<ANDROID-API>-<ANDROID-ABIs>/<PACKAGE-NAME> [OPTIONS]${COLOR_OFF}
    export the given installed package as google prefab aar.

    For details about google prefab, please visit https://google.github.io/prefab/

    For details about the aar file contains google prefab, please visit https://developer.android.com/studio/projects/android-library#aar-contents

    ${COLOR_BLUE}<ANDROID-ABIs>${COLOR_OFF}
        A comma-separated list, any combination of arm64-v8a, armeabi-v7a, x86_64, x86

    ${COLOR_BLUE}--ndk-home=<ANDROID-NDK-HOME>${COLOR_OFF}
        If this option is unspecified, a proper version of Android NDK will be automatically installed for you via uppm.

    ${COLOR_BLUE}-o <OUTPUT-PATH>${COLOR_OFF}
        specify where the aar file will be written to.

        <OUTPUT-PATH> can be either the filepath or directory.

        If <OUTPUT-PATH> is an existing directory or ends with slash, then it will be treated as a directory, otherwise, it will be treated as a filepath.

        If <OUTPUT-PATH> is treated as a directory, then it would be expanded to <OUTPUT-PATH>/<PACKAGE-NAME>-<PACKAGE-VERSION>.aar

    ${COLOR_BLUE}-K${COLOR_OFF}
        keep the session directory even if this package is successfully exported.

${COLOR_GREEN}ndk-pkg deploy android-<ANDROID-API>-<ANDROID-ABIs>/<PACKAGE-NAME> [OPTIONS]${COLOR_OFF}
    export the given installed package as google prefab aar then deploy it to Maven Repository.

    For details about google prefab, please visit https://google.github.io/prefab/

    For details about the aar file contains google prefab, please visit https://developer.android.com/studio/projects/android-library#aar-contents

    ${COLOR_BLUE}<ANDROID-ABIs>${COLOR_OFF}
        A comma-separated list, any combination of arm64-v8a, armeabi-v7a, x86_64, x86

    ${COLOR_BLUE}--ndk-home=<ANDROID-NDK-HOME>${COLOR_OFF}
        If this option is unspecified, a proper version of Android NDK will be automatically installed for you via uppm.

    ${COLOR_BLUE}--debug${COLOR_OFF}
        pass -X option to mvn command.

    ${COLOR_BLUE}--dry-run${COLOR_OFF}
        just pack the aar file, do not actually export.

    ${COLOR_BLUE}-K${COLOR_OFF}
        keep the session directory even if this package is successfully exported.

    ${COLOR_BLUE}--groupId=<GROUPID>${COLOR_OFF}
        specify the groupId.

        If this option is unspecified, ${COLOR_RED}com.fpliu.ndk.pkg.prefab.android.<ANDROID-API>${COLOR_OFF} would be used as default.

    ${COLOR_BLUE}--local=<DIR>${COLOR_OFF}
        deploy the aar file to the specified location of Maven Local Repository.

    ${COLOR_BLUE}--remote${COLOR_OFF}
        deploy the aar file to Sonatype OSSRH (https://s01.oss.sonatype.org/)

        If this option is specified, some extra information would be read from stdin. stdin should contain the following content:

        SERVER_ID=
        SERVER_URL=
        SERVER_USERNAME=
        SERVER_PASSWORD=
        GPG_PASSPHRASE=

    ${COLOR_RED}Caveat:${COLOR_OFF} If neither --local=<DIR> nor --remote is specified, the aar file would be deployed to your default Maven Local Repository (usually under ~/.m2/repository/ directory, depending on your settings.xml configuration)


==================
Naming explanation
==================

${COLOR_GREEN}<PACKAGE-SPEC>${COLOR_OFF}
    a formatted string that has form: ${COLOR_RED}<TARGET>/<PACKAGE-NAME>${COLOR_OFF}, represents an installed package.

${COLOR_GREEN}<PACKAGE-NAME>${COLOR_OFF}
    must match the regular expression pattern ${COLOR_RED}^[A-Za-z0-9+-_.@]{1,50}\$${COLOR_OFF}

${COLOR_GREEN}<TARGET>${COLOR_OFF}
    a formatted string that has form: ${COLOR_RED}android-<ANDROID-API>-<ANDROID-ABI>${COLOR_OFF}

${COLOR_GREEN}<ANDROID-API>${COLOR_OFF}
    indicates which minimum Android SDK API level was built with.

    Reference: https://developer.android.com/tools/releases/platforms

${COLOR_GREEN}<ANDROID-ABI>${COLOR_OFF}
    indicates which Android ABI was built for.

    Reference: https://developer.android.com/ndk/guides/abis


=====================
Environment variables
=====================

${COLOR_GREEN}HOME${COLOR_OFF}
    This environment variable already have been set on the most operating systems, if not set or set a empty string, you may receive an error message.

${COLOR_GREEN}PATH${COLOR_OFF}
    This environment variable already have been set on the most operating systems, if not set or set a empty string, you may receive an error message.

${COLOR_GREEN}SSL_CERT_FILE${COLOR_OFF}
    In general, you don't need to set this environment variable, but, if you encounter the reporting ${COLOR_RED}the SSL certificate is invalid${COLOR_OFF}, trying to run below commands in your terminal will do the trick.

    ${COLOR_RED}curl -LO https://curl.se/ca/cacert.pem
    export SSL_CERT_FILE="\$PWD/cacert.pem"${COLOR_OFF}

${COLOR_GREEN}GOPROXY${COLOR_OFF}
    Example:

    ${COLOR_RED}export GOPROXY='https://goproxy.cn'${COLOR_OFF}

${COLOR_GREEN}NDKPKG_HOME${COLOR_OFF}
    ndk-pkg would generate all data under ${COLOR_RED}~/.ndk-pkg${COLOR_OFF} directory by default, you could change it by setting this environment variable.

    Example:

    ${COLOR_RED}export NDKPKG_HOME=/path/of/ndk-pkg-home${COLOR_OFF}

${COLOR_GREEN}NDKPKG_URL_TRANSFORM${COLOR_OFF}
    If you want to change the request url, you can set this environment variable. It is very useful for chinese users.

    Example:

    ${COLOR_RED}export NDKPKG_URL_TRANSFORM=/path/of/url-transform${COLOR_OFF}

    /path/of/url-transform command would be invoked as /path/of/url-transform <URL>

    /path/of/url-transform command must output a <URL>

    a url-transform sample can be generated via running ${COLOR_RED}ndk-pkg gen-url-transform-sample${COLOR_OFF}

${COLOR_GREEN}NDKPKG_XTRACE${COLOR_OFF}
    for debugging purposes. to enable set -x: ${COLOR_RED}export NDKPKG_XTRACE=1${COLOR_OFF}

${COLOR_GREEN}NDKPKG_DEFAULT_TARGET${COLOR_OFF}
    some ACTIONs of ndk-pkg need <PACKAGE-SPEC> to be specified. To simplify the usage, you are allowed to omit <TARGET>/. If <TARGET>/ is omitted, environment variable NDKPKG_DEFAULT_TARGET would be checked, if it is not set, then ${COLOR_RED}android-21-aarm64-v8a${COLOR_OFF} will be used as the default.

    Example:

    ${COLOR_RED}export NDKPKG_DEFAULT_TARGET='android-21-arm64-v8a'${COLOR_OFF}


=======
Caveats
=======

This software is being actively developed. It's in beta stage and may not be stable. Some features are subject to change without notice.

Please do NOT place your own files under ~/.ndk-pkg directory, as ndk-pkg will change files under ~/.ndk-pkg directory without notice.

Please do NOT run ndk-pkg command in parallel so as not to generate dirty data.
    "

    if [ "$(uname -s)" = Linux ] ; then
        if [ -f /etc/os-release ] ; then
            .   /etc/os-release
        fi

        if [ "$ID" = alpine ] ; then
            printf 'Due to Android NDK for Linux is linked dynamically against glibc, hence you might need to install glibc by yourself on this system(alpine-%s), to achieve this, please refer to https://wiki.alpinelinux.org/wiki/Running_glibc_programs\n\n' "$VERSION_ID"
        fi
    fi

    if [ -z "$1" ] ; then
        exit
    else
        exit "$1"
    fi
}

# }}}
##############################################################################
# {{{ ndk-pkg main

set -e

# If IFS is not set, the default value will be <space><tab><newline>
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_03
unset IFS

##################################################################################

if [ -n "$NDKPKG_XTRACE" ] ; then
    set -x
fi

[ -z "$NDKPKG_HOME" ] && {
    if [ -z "$HOME" ] ; then
        abort 1 "HOME environment variable is not set."
    else
        export NDKPKG_HOME="$HOME/.ndk-pkg"
    fi
}

NDKPKG_VERSION=0.19.2

NDKPKG_ARG0="$0"
NDKPKG_ARG1="$1"
NDKPKG_ARGV="$0 $*"

NDKPKG_PATH="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"

NDKPKG_ZSH_COMPLETION_SCRIPT_URL='https://raw.githubusercontent.com/leleliu008/ndk-pkg/master/ndk-pkg-zsh-completion'
NDKPKG_OFFICIAL_FORMULA_REPO_URL='https://github.com/leleliu008/ndk-pkg-formula-repository-official-core.git'
NDKPKG_UPGRAGE_URL='https://raw.githubusercontent.com/leleliu008/ndk-pkg/master/ndk-pkg'

NDKPKG_CORE_DIR="$NDKPKG_HOME/core"

NDKPKG_DOWNLOADS_DIR="$NDKPKG_HOME/downloads"

NDKPKG_FORMULA_REPO_ROOT="$NDKPKG_HOME/repos.d"

NDKPKG_FORMULA_SEARCH_DIRS=

NDKPKG_PACKAGE_INSTALLED_ROOT="$NDKPKG_HOME/installed"

##################################################################################

NATIVE_PACKAGE_INSTALLED_ROOT="$NDKPKG_HOME/native"

##################################################################################

UPPM="$NDKPKG_CORE_DIR/uppm"

export UPPM_HOME="$NDKPKG_HOME/uppm"

uppm() {
    run "$UPPM" "$@"
}

if [ -n "$NDKPKG_URL_TRANSFORM" ] ; then
    export  UPPM_URL_TRANSFORM="$NDKPKG_URL_TRANSFORM"
    export       URL_TRANSFORM="$NDKPKG_URL_TRANSFORM"
fi

unset CURLOPT_DNS_SERVERS

if [ "$(uname -o 2>/dev/null || true)" = Android ] ; then
    LV="$(getprop ro.build.version.sdk)"
    export UPPM_FORMULA_REPO_URL_OFFICIAL_CORE="https://github.com/fpliu1214/uppm-package-repository-android-$LV-aarch64"

    if [ -z "$NDKPKG_DNS_SERVERS" ] ; then
        NDKPKG_DNS_SERVERS='1.1.1.1,8.8.8.8'
    fi

    export CURLOPT_DNS_SERVERS="$NDKPKG_DNS_SERVERS"

    if command -v termux-info > /dev/null ; then
        TERMUX=1
    else
        unset TERMUX
    fi
fi

##################################################################################

# https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_default_verify_paths.html
if [ -f "$NDKPKG_CORE_DIR/cacert.pem" ] ; then
    export SSL_CERT_FILE="$NDKPKG_CORE_DIR/cacert.pem"
fi

##################################################################################

# dot_static command use this
# https://www.freedesktop.org/software/fontconfig/fontconfig-user.html
if [ -z  "$FONTCONFIG_FILE" ] ; then
    export FONTCONFIG_FILE="$NDKPKG_CORE_DIR/fonts.conf"
fi

##################################################################################

TIMESTAMP_UNIX="$(date +%s)"

##################################################################################

export BAT_THEME=Dracula

##################################################################################

case $1 in
    ''|--help|-h)
        __help
        exit
        ;;
    --version|-V)
        printf '%s\n' "$NDKPKG_VERSION"
        exit
        ;;
    about)
        if command -v bat > /dev/null ; then
            VIEWER='bat --language=yaml --paging=never --color=always --theme=Dracula --style=plain'
        else
            VIEWER=cat
        fi

        $VIEWER <<EOF
ndk-pkg.version: $NDKPKG_VERSION
ndk-pkg.homedir: $NDKPKG_HOME
ndk-pkg.exepath: $NDKPKG_PATH
ndk-pkg.website: https://github.com/leleliu008/ndk-pkg
EOF
        if [ -f "$UPPM" ] ; then
            printf '\n'
            "$UPPM" about
        fi

        exit
        ;;
    setup)
        shift
        __setup "$@"
        exit
        ;;
    gen-url-transform-sample)
        shift
        __gen_url_transform_sample "$@"
        exit
        ;;
    android-versions)
        # https://apilevels.com/
        cat <<EOF
Android 15          35  VANILLA_ICE_CREAM
Android 14          34  UPSIDE_DOWN_CAKE
Android 13          33  TIRAMISU
Android 12          32  S_V2
Android 12          31  S
Android 11          30  R
Android 10          29  Q
Android 9           28  P
Android 8.1         27  O_MR1
Android 8.0         26  O
Android 7.1         25  N_MR1
Android 7.0         24  N
Android 6.0         23  M
Android 5.1         22  LOLLIPOP_MR1
Android 5.0         21  LOLLIPOP
Android 4.4W        20  KITKAT_WATCH
Android 4.4         19  KITKAT
Android 4.3         18  JELLY_BEAN_MR2
Android 4.2         17  JELLY_BEAN_MR1
Android 4.1         16  JELLY_BEAN
Android 4.0.3,4     15  ICE_CREAM_SANDWICH_MR1
Android 4.0,.1,.2   14  ICE_CREAM_SANDWICH
Android 3.2         13  HONEYCOMB_MR2
Android 3.1.x       12  HONEYCOMB_MR1
Android 3.0.x       11  HONEYCOMB
Android 2.3.3,.4    10  GINGERBREAD_MR1
Android 2.3,.1,.2   9   GINGERBREAD
Android 2.2.x       8   FROYO
Android 2.1.x       7   ECLAIR_MR1
Android 2.0.1       6   ECLAIR_0_1
Android 2.0         5   ECLAIR
Android 1.6         4   DONUT
Android 1.5         3   CUPCAKE
Android 1.1         2   BASE_1_1
Android 1.0         1   BASE
EOF
        exit
        ;;
esac

##################################################################################

[ -z  "$CARGO_HOME" ] && {
    if [ -z "$HOME" ] ; then
        abort 1 "HOME environment variable is not set."
    else
        CARGO_HOME="$HOME/.cargo"
    fi
}

bppend_to_PATH "$CARGO_HOME/bin"

##################################################################################

if [ -d         "$NDKPKG_CORE_DIR/bin" ] ; then
    export PATH="$NDKPKG_CORE_DIR/bin:$PATH"
fi

##################################################################################

if [ -f "$NDKPKG_CORE_DIR/init.sh" ] ; then
    .   "$NDKPKG_CORE_DIR/init.sh"
else
    abort 1 "please run ${COLOR_GREEN}$NDKPKG_ARG0 setup${COLOR_OFF} command first, then try again."
fi

##################################################################################

# https://www.gnu.org/software/automake/manual/html_node/Macro-Search-Path.html
if [ -z  "$ACLOCAL_PATH" ] ; then
    export ACLOCAL_PATH="$NDKPKG_CORE_DIR/share/aclocal"
else
    export ACLOCAL_PATH="$NDKPKG_CORE_DIR/share/aclocal:$ACLOCAL_PATH"
fi

##################################################################################

NATIVE_PLATFORM_KIND="$(sysinfo kind)"
NATIVE_PLATFORM_TYPE="$(sysinfo type)"
NATIVE_PLATFORM_CODE="$(sysinfo code)"
NATIVE_PLATFORM_NAME="$(sysinfo name)"
NATIVE_PLATFORM_VERS="$(sysinfo vers)"
NATIVE_PLATFORM_ARCH="$(sysinfo arch)"
NATIVE_PLATFORM_NCPU="$(sysinfo ncpu)"
NATIVE_PLATFORM_LIBC="$(sysinfo libc)"
NATIVE_PLATFORM_EUID="$(id -u)"
NATIVE_PLATFORM_EGID="$(id -g)"

if [ "$NATIVE_PLATFORM_TYPE" = linux ] ; then
    if [ -f '/lib/ld-musl-x86_64.so.1' ] ; then
        NATIVE_PLATFORM_LIBC=musl
    elif [ -f '/lib64/ld-linux-x86-64.so.2' ] ; then
        NATIVE_PLATFORM_LIBC=glibc
    fi
fi

##################################################################################

if [ "$NATIVE_PLATFORM_EUID" -ne 0 ] ; then
    sudo=sudo
fi

##################################################################################

case $1 in
    ndkinfo)
        shift
        inspect_android_ndk_info "$1"
        println_android_ndk_info
        ;;

    sysinfo)
        shift
        sysinfo "$@"
        ;;

    update)            shift; __sync_available_formula_repositories "$@" ;;
    formula-repo-list) shift; __list_available_formula_repositories "$@" ;;
    formula-repo-info) shift; __info_the_given_formula_repository "$@" ;;
    formula-repo-conf) shift; __conf_the_given_formula_repository "$@" ;;
    formula-repo-sync) shift; __sync_the_given_formula_repository "$@" ;;
    formula-repo-add)
        shift

        case $1 in
            official-*) abort 1 "ndk-pkg formula repository name that starts with 'official-' is reserved for ndk-pkg official formula repository, please use other name."
        esac

        __create_a_formula_repository "$@"
        ;;
    formula-repo-del)
        shift
        __delete_a_formula_repository "$@"
        ;;

    info-available) shift; __info_the_given_available_package "$@" ;;
    info-installed) shift; __info_the_given_installed_package "$@" ;;

    ls-available) shift; __list_available_packages "$@" ;;
    ls-installed) shift; __list_installed_packages "$@" ;;
    ls-outdated)  shift; __list__outdated_packages "$@" ;;

    is-available) shift; is_package_available "$@" ;;

    is-installed)
        shift
        PACKAGE_SPEC=
        PACKAGE_SPEC="$(inspect_package_spec "$1")"
        is_package_installed "$PACKAGE_SPEC"
        ;;

    is-outdated)
        shift
        PACKAGE_SPEC=
        PACKAGE_SPEC="$(inspect_package_spec "$1")"
        is_package__outdated "$PACKAGE_SPEC"
        ;;

    search)  shift; __search_packages "$@" ;;

    depends) shift; __show_packages_depended_by_the_given_package "$@" ;;
    fetch)   shift;        __fetch_resources_of_the_given_package "$@" ;;

    install) shift;   __install_the_given_packages "$@" ;;
  reinstall) shift; __reinstall_the_given_packages "$@" ;;
  uninstall) shift; __uninstall_the_given_packages "$@" ;;

    upgrade) shift; __upgrade_packages "$@" ;;

    upgrade-self)
             shift; __upgrade_self "$NDKPKG_UPGRAGE_URL" "$@" ;;

    logs) shift; __logs_the_given_installed_package "$@" ;;
    tree) shift; __tree_the_given_installed_package "$@" ;;
    pack) shift; __pack_the_given_installed_package "$@" ;;

    export) shift; __export_google_prefab_aar_for_the_given_installed_packages "$@" ;;
    deploy) shift; __deploy_google_prefab_aar_for_the_given_installed_packages "$@" ;;

    integrate)
        shift
        case $1 in
            zsh)
                shift
                __integrate_zsh_completions "$NDKPKG_ZSH_COMPLETION_SCRIPT_URL" "$@"
                ;;
            *)  abort 1 "ndk-pkg integrate $1: not support."
        esac
        ;;

    cleanup) shift; __cleanup ;;

    run)
        shift

        case $1 in
            uppm) shift; "$UPPM" "$@" ;;
            '')   abort 1 'no command is supplied to run.' ;;
            *)    "$@"
        esac
        ;;

    *)  abort 1 "unrecognized argument: $1"
esac

# }}}