Files
T

277 lines
8.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Copyright 2026 Mocktail Project Authors
#
# 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
#
# http://www.apache.org/licenses/LICENSE-2.0
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
RESET='\033[0m'
info() { echo -e "${CYAN}[info]${RESET} $*"; }
success() { echo -e "${GREEN}[ok]${RESET} $*"; }
warn() { echo -e "${YELLOW}[warn]${RESET} $*"; }
die() { echo -e "${RED}[error]${RESET} $*" >&2; exit 1; }
step() { echo; echo -e "${BOLD}[$*]${RESET}"; }
Usage() {
cat <<'EOF'
Usage: scripts/build.sh [OPTIONS]
With no APK option, this helper builds only the native runtime. The first
normal launch downloads, validates, canaries, and activates the supported
Roblox x86_64 payload automatically. The ordinary entry point is `make build`,
which invokes CMake directly without this helper.
Options:
--apk PATH Explicitly extract a local Roblox x86_64 APK.
--apk-url URL Explicitly download and extract an APK URL.
--skip-apk Compatibility alias for managed-payload mode.
--build-dir PATH CMake build directory (default: build).
--cmake-toolchain FILE
Explicit CMake toolchain file.
--cmake-sysroot DIR Explicit compiler/CMake sysroot.
--build-type TYPE Debug, Release, or RelWithDebInfo (default: Release).
--jobs N Parallel build jobs (default: nproc).
--run-tests Run CTest after building.
--clean Remove the selected build directory first.
-h, --help Show this help.
Examples:
make build
./scripts/build.sh
./scripts/build.sh --build-type Debug --run-tests
./scripts/build.sh --apk /path/to/roblox-x86_64.apk
EOF
}
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
BUILD_DIR="${PROJECT_ROOT}/build"
RBX_BIN_DIR="${PROJECT_ROOT}/rbx_bin"
APK_PATH=""
APK_URL=""
EXTRACT_APK=false
EXPLICIT_RUNTIME_OVERRIDE=false
BUILD_TYPE="Release"
JOBS="$(nproc)"
RUN_TESTS=false
CLEAN=false
CMAKE_TOOLCHAIN_FILE=""
CMAKE_SYSROOT=""
while [[ $# -gt 0 ]]; do
case "$1" in
--apk)
[[ $# -ge 2 ]] || die "--apk requires a path"
APK_PATH="$2"
EXTRACT_APK=true
shift 2
;;
--apk-url)
[[ $# -ge 2 ]] || die "--apk-url requires a URL"
APK_URL="$2"
EXTRACT_APK=true
shift 2
;;
--skip-apk)
EXTRACT_APK=false
shift
;;
--build-dir)
[[ $# -ge 2 ]] || die "--build-dir requires a path"
BUILD_DIR="$2"
shift 2
;;
--cmake-toolchain)
[[ $# -ge 2 ]] || die "--cmake-toolchain requires a file"
CMAKE_TOOLCHAIN_FILE="$2"
shift 2
;;
--cmake-sysroot)
[[ $# -ge 2 ]] || die "--cmake-sysroot requires a directory"
CMAKE_SYSROOT="$2"
shift 2
;;
--build-type) BUILD_TYPE="$2"; shift 2 ;;
--jobs) JOBS="$2"; shift 2 ;;
--run-tests) RUN_TESTS=true; shift ;;
--clean) CLEAN=true; shift ;;
--help|-h)
Usage
exit 0
;;
*)
die "Unknown option: $1 (try --help)"
;;
esac
done
if [[ "${EXTRACT_APK}" == true && -n "${APK_PATH}" && -n "${APK_URL}" ]]; then
die "--apk and --apk-url are mutually exclusive"
fi
if [[ "${BUILD_DIR}" != /* ]]; then
BUILD_DIR="${PROJECT_ROOT}/${BUILD_DIR}"
fi
BUILD_DIR="$(readlink -m -- "${BUILD_DIR}")"
[[ "${BUILD_DIR}" != / && "${BUILD_DIR}" != "${PROJECT_ROOT}" ]] ||
die "Unsafe build directory: ${BUILD_DIR}"
if [[ -n "${CMAKE_TOOLCHAIN_FILE}" ]]; then
[[ -f "${CMAKE_TOOLCHAIN_FILE}" ]] ||
die "CMake toolchain file not found: ${CMAKE_TOOLCHAIN_FILE}"
CMAKE_TOOLCHAIN_FILE="$(readlink -f -- "${CMAKE_TOOLCHAIN_FILE}")"
fi
if [[ -n "${CMAKE_SYSROOT}" ]]; then
[[ -d "${CMAKE_SYSROOT}" ]] ||
die "CMake sysroot not found: ${CMAKE_SYSROOT}"
CMAKE_SYSROOT="$(cd -P -- "${CMAKE_SYSROOT}" && pwd)"
fi
info "Mocktail build: ${BUILD_TYPE}, -j${JOBS}"
info "Project root: ${PROJECT_ROOT}"
info "Build directory: ${BUILD_DIR}"
step "1/5 checking dependencies"
check_tool() {
local tool="$1"
local hint="${2:-install ${tool}}"
if command -v "${tool}" &>/dev/null; then
success "${tool} found: $(command -v "${tool}")"
else
die "${tool} not found. Hint: ${hint}"
fi
}
check_tool cmake "install CMake 3.20 or newer"
if [[ -z "${CMAKE_TOOLCHAIN_FILE}" ]]; then
check_tool c++ "install GCC or Clang with C++17 support"
fi
check_tool git "sudo apt install git"
check_tool java "sudo apt install default-jdk"
check_tool ld.lld "install LLD"
check_tool elfedit "install binutils"
if [[ "${EXTRACT_APK}" == true ]]; then
check_tool unzip "sudo apt install unzip"
fi
if [[ "${EXTRACT_APK}" == true && -n "${APK_URL}" ]] &&
! command -v curl &>/dev/null; then
check_tool wget "sudo apt install wget"
fi
if [[ "${EXTRACT_APK}" == true && -n "${APK_URL}" ]]; then
step "2/5 downloading Roblox APK"
APK_PATH="${PROJECT_ROOT}/rbx_bin/roblox-x86_64.apk"
mkdir -p "${RBX_BIN_DIR}"
info "URL: ${APK_URL}"
info "Destination: ${APK_PATH}"
if command -v curl &>/dev/null; then
curl -L --progress-bar -o "${APK_PATH}" "${APK_URL}"
else
wget -q --show-progress -O "${APK_PATH}" "${APK_URL}"
fi
success "APK downloaded."
fi
LIBROBLOX_DST="${RBX_BIN_DIR}/libroblox.so"
if [[ -n "${ROBLOX_LIB_PATH:-}" ]]; then
info "Explicit runtime library override detected — APK extraction disabled."
EXTRACT_APK=false
EXPLICIT_RUNTIME_OVERRIDE=true
fi
if [[ "${EXTRACT_APK}" == true ]]; then
step "3/5 extracting libroblox.so"
[[ -n "${APK_PATH}" ]] || die "explicit APK extraction has no input"
[[ -f "${APK_PATH}" ]] || die "APK not found: ${APK_PATH}"
mkdir -p "${RBX_BIN_DIR}"
info "APK: ${APK_PATH}"
info "Looking for lib/x86_64/libroblox.so inside the archive…"
if ! unzip -l "${APK_PATH}" | grep -q "lib/x86_64/libroblox.so"; then
die "APK does not contain lib/x86_64/libroblox.so; use an x86_64 split APK"
else
unzip -jo "${APK_PATH}" "lib/x86_64/libroblox.so" -d "${RBX_BIN_DIR}"
success "Extracted → ${LIBROBLOX_DST}"
fi
else
step "2/5 selecting managed Roblox payload"
success "No APK or libroblox.so is required at build time."
fi
step "4/5 initialising git submodules"
cd "${PROJECT_ROOT}"
if [[ -f ".gitmodules" ]]; then
git submodule update --init --recursive
success "Submodules ready."
else
warn "No .gitmodules found — skipping submodule init."
info "To add submodules run: ./scripts/add_submodules.sh"
fi
step "5/5 configuring CMake"
if [[ "${CLEAN}" == true && -d "${BUILD_DIR}" ]]; then
info "Removing old build directory…"
rm -rf "${BUILD_DIR}"
fi
if [[ "${RUN_TESTS}" == true ]]; then
CMAKE_BUILD_TESTING=ON
else
CMAKE_BUILD_TESTING=OFF
fi
cmake_arguments=(
-S "${PROJECT_ROOT}"
-B "${BUILD_DIR}"
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}"
-DBUILD_TESTING="${CMAKE_BUILD_TESTING}"
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
)
if [[ -n "${CMAKE_TOOLCHAIN_FILE}" ]]; then
cmake_arguments+=("-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}")
fi
if [[ -n "${CMAKE_SYSROOT}" ]]; then
cmake_arguments+=("-DCMAKE_SYSROOT=${CMAKE_SYSROOT}")
fi
cmake "${cmake_arguments[@]}"
success "CMake configuration complete."
step "building Mocktail"
cmake --build "${BUILD_DIR}" -j"${JOBS}"
success "Build complete → ${BUILD_DIR}/mocktail"
if [[ "${RUN_TESTS}" == true ]]; then
step "running unit tests"
ctest --test-dir "${BUILD_DIR}" --output-on-failure -j"${JOBS}"
success "All tests passed."
fi
echo
success "Build successful."
echo "Run:"
echo -e " ${CYAN}${BUILD_DIR}/mocktail${RESET}"
if [[ "${EXPLICIT_RUNTIME_OVERRIDE}" == true ]]; then
info "The explicit runtime library override remains active for this shell."
elif [[ "${EXTRACT_APK}" == false ]]; then
info "First launch downloads, verifies, and activates the supported Roblox x86_64 payload automatically."
else
info "Explicit APK extraction completed; managed first-run updates remain available."
fi
echo