272 lines
7.3 KiB
Bash
Executable File
272 lines
7.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$SCRIPT_DIR"
|
|
TMP_DIR="$PROJECT_DIR/.tmp-android-build"
|
|
SDK_DIR="$PROJECT_DIR/android-sdk"
|
|
JDK_DIR="$PROJECT_DIR/jdk"
|
|
STATE_FILE="$PROJECT_DIR/.setup-build-environment.state"
|
|
CMDLINE_TOOLS_DIR="$SDK_DIR/cmdline-tools"
|
|
CMDLINE_TOOLS_LATEST_DIR="$CMDLINE_TOOLS_DIR/latest"
|
|
WRAPPER_JAR_PATH="$PROJECT_DIR/gradle/wrapper/gradle-wrapper.jar"
|
|
LOCAL_PROPERTIES_PATH="$PROJECT_DIR/local.properties"
|
|
|
|
ANDROID_CMDLINE_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"
|
|
GRADLE_DIST_URL="https://services.gradle.org/distributions/gradle-8.7-bin.zip"
|
|
JDK_DIST_URL="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jdk/hotspot/normal/eclipse"
|
|
STATE_TTL_SECONDS=86400
|
|
|
|
log() {
|
|
printf '\n[%s] %s\n' "setup" "$1"
|
|
}
|
|
|
|
require_tool() {
|
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
echo "Missing required tool: $1" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
ensure_dir() {
|
|
mkdir -p "$1"
|
|
}
|
|
|
|
current_epoch() {
|
|
date +%s
|
|
}
|
|
|
|
have_recent_success_marker() {
|
|
if [ ! -f "$STATE_FILE" ]; then
|
|
return 1
|
|
fi
|
|
|
|
local recorded_epoch
|
|
recorded_epoch="$(cat "$STATE_FILE" 2>/dev/null || true)"
|
|
if ! printf '%s' "$recorded_epoch" | grep -Eq '^[0-9]+$'; then
|
|
return 1
|
|
fi
|
|
|
|
local now
|
|
now="$(current_epoch)"
|
|
[ $((now - recorded_epoch)) -lt "$STATE_TTL_SECONDS" ]
|
|
}
|
|
|
|
mark_successful_check() {
|
|
current_epoch > "$STATE_FILE"
|
|
}
|
|
|
|
auto_commit_if_needed() {
|
|
if ! command -v git >/dev/null 2>&1; then
|
|
log "Git is not available; skipping automatic commit"
|
|
return
|
|
fi
|
|
|
|
if ! git -C "$PROJECT_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
log "Project is not inside a git work tree; skipping automatic commit"
|
|
return
|
|
fi
|
|
|
|
git -C "$PROJECT_DIR" add -A
|
|
|
|
if git -C "$PROJECT_DIR" diff --cached --quiet; then
|
|
log "No source changes to commit after successful build"
|
|
return
|
|
fi
|
|
|
|
local changed_files
|
|
changed_files="$(git -C "$PROJECT_DIR" diff --cached --name-only)"
|
|
|
|
local -a summary_parts=()
|
|
if printf '%s\n' "$changed_files" | grep -q '^app/src/main/java/'; then
|
|
summary_parts+=("update app gameplay/UI")
|
|
fi
|
|
if printf '%s\n' "$changed_files" | grep -Eq '^(SetupBuildEnvironment\.sh|gradle\.properties|gradle/|gradlew|gradlew\.bat|build\.gradle\.kts|settings\.gradle\.kts|app/build\.gradle\.kts)'; then
|
|
summary_parts+=("improve build setup")
|
|
fi
|
|
if printf '%s\n' "$changed_files" | grep -Eq '^(README\.md|\.gitignore)$'; then
|
|
summary_parts+=("update docs/config")
|
|
fi
|
|
if [ ${#summary_parts[@]} -eq 0 ]; then
|
|
summary_parts+=("project updates")
|
|
fi
|
|
|
|
local commit_message
|
|
commit_message="Auto-commit after successful build: $(printf '%s, ' "${summary_parts[@]}" | sed 's/, $//')"
|
|
log "Creating git commit for the latest successful build"
|
|
git -C "$PROJECT_DIR" commit -m "$commit_message" -m "Changed files:\n$changed_files"
|
|
}
|
|
|
|
download_if_missing() {
|
|
local url="$1"
|
|
local output="$2"
|
|
if [ -f "$output" ]; then
|
|
log "Using existing download: ${output#$PROJECT_DIR/}"
|
|
else
|
|
log "Downloading $(basename "$output")"
|
|
curl -L "$url" -o "$output"
|
|
fi
|
|
}
|
|
|
|
ensure_cmdline_tools() {
|
|
if [ -x "$CMDLINE_TOOLS_LATEST_DIR/bin/sdkmanager" ]; then
|
|
log "Android command-line tools already present"
|
|
return
|
|
fi
|
|
|
|
ensure_dir "$CMDLINE_TOOLS_DIR"
|
|
ensure_dir "$TMP_DIR"
|
|
local zip_path="$TMP_DIR/commandlinetools.zip"
|
|
download_if_missing "$ANDROID_CMDLINE_TOOLS_URL" "$zip_path"
|
|
|
|
log "Extracting Android command-line tools"
|
|
rm -rf "$CMDLINE_TOOLS_LATEST_DIR"
|
|
unzip -q -o "$zip_path" -d "$CMDLINE_TOOLS_DIR"
|
|
if [ -d "$CMDLINE_TOOLS_DIR/cmdline-tools" ]; then
|
|
mv "$CMDLINE_TOOLS_DIR/cmdline-tools" "$CMDLINE_TOOLS_LATEST_DIR"
|
|
fi
|
|
}
|
|
|
|
ensure_wrapper_jar() {
|
|
if [ -f "$WRAPPER_JAR_PATH" ]; then
|
|
log "Gradle wrapper jar already present"
|
|
return
|
|
fi
|
|
|
|
ensure_dir "$TMP_DIR"
|
|
local zip_path="$TMP_DIR/gradle-8.7-bin.zip"
|
|
local gradle_extract_dir="$TMP_DIR/gradle-8.7"
|
|
local plugin_wrapper_jar="$gradle_extract_dir/lib/plugins/gradle-wrapper-8.7.jar"
|
|
download_if_missing "$GRADLE_DIST_URL" "$zip_path"
|
|
|
|
log "Extracting Gradle wrapper jar"
|
|
rm -rf "$gradle_extract_dir"
|
|
unzip -q -o "$zip_path" -d "$TMP_DIR"
|
|
python - <<PY
|
|
import zipfile
|
|
src = r'''$plugin_wrapper_jar'''
|
|
dst = r'''$WRAPPER_JAR_PATH'''
|
|
with zipfile.ZipFile(src) as zf:
|
|
data = zf.read('gradle-wrapper.jar')
|
|
with open(dst, 'wb') as f:
|
|
f.write(data)
|
|
PY
|
|
}
|
|
|
|
ensure_jdk17() {
|
|
if [ -x "$JDK_DIR/bin/java" ]; then
|
|
log "Project-local JDK already present"
|
|
return
|
|
fi
|
|
|
|
ensure_dir "$TMP_DIR"
|
|
local archive_path="$TMP_DIR/jdk17.tar.gz"
|
|
local extract_dir="$TMP_DIR/jdk17-extract"
|
|
rm -f "$archive_path"
|
|
download_if_missing "$JDK_DIST_URL" "$archive_path"
|
|
|
|
log "Extracting project-local JDK"
|
|
rm -rf "$extract_dir" "$JDK_DIR"
|
|
mkdir -p "$extract_dir"
|
|
if ! file "$archive_path" | grep -qiE 'gzip compressed|tar archive'; then
|
|
echo "Downloaded JDK archive is not a valid tar.gz: $archive_path" >&2
|
|
exit 1
|
|
fi
|
|
tar -xzf "$archive_path" -C "$extract_dir"
|
|
local extracted_root
|
|
extracted_root="$(find "$extract_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1)"
|
|
if [ -z "$extracted_root" ]; then
|
|
echo "Unable to locate extracted JDK directory" >&2
|
|
exit 1
|
|
fi
|
|
mv "$extracted_root" "$JDK_DIR"
|
|
}
|
|
|
|
setup_java_env() {
|
|
export JAVA_HOME="$JDK_DIR"
|
|
export PATH="$JAVA_HOME/bin:$PATH"
|
|
}
|
|
|
|
write_local_properties() {
|
|
log "Writing local.properties"
|
|
cat > "$LOCAL_PROPERTIES_PATH" <<EOF_INNER
|
|
sdk.dir=$SDK_DIR
|
|
EOF_INNER
|
|
}
|
|
|
|
setup_env() {
|
|
setup_java_env
|
|
export ANDROID_HOME="$SDK_DIR"
|
|
export ANDROID_SDK_ROOT="$SDK_DIR"
|
|
}
|
|
|
|
ensure_sdk_packages() {
|
|
setup_env
|
|
|
|
if have_recent_success_marker \
|
|
&& [ -d "$SDK_DIR/platform-tools" ] \
|
|
&& [ -d "$SDK_DIR/platforms/android-34" ] \
|
|
&& [ -d "$SDK_DIR/build-tools/34.0.0" ]; then
|
|
log "Skipping SDK verification/install because all components were confirmed within the last 24 hours"
|
|
return
|
|
fi
|
|
|
|
log "Accepting Android SDK licenses"
|
|
set +e
|
|
set +o pipefail
|
|
yes | "$CMDLINE_TOOLS_LATEST_DIR/bin/sdkmanager" --sdk_root="$SDK_DIR" --licenses >/dev/null
|
|
local license_status=$?
|
|
set -o pipefail
|
|
set -e
|
|
if [ "$license_status" -ne 0 ]; then
|
|
echo "sdkmanager --licenses failed with exit code $license_status" >&2
|
|
exit "$license_status"
|
|
fi
|
|
|
|
log "Installing required Android SDK packages"
|
|
"$CMDLINE_TOOLS_LATEST_DIR/bin/sdkmanager" --sdk_root="$SDK_DIR" \
|
|
"platform-tools" \
|
|
"platforms;android-34" \
|
|
"build-tools;34.0.0"
|
|
|
|
mark_successful_check
|
|
}
|
|
|
|
maybe_build() {
|
|
if [ "${1:-}" != "--build" ]; then
|
|
return
|
|
fi
|
|
|
|
setup_env
|
|
log "Building debug APK with --no-daemon"
|
|
"$PROJECT_DIR/gradlew" --no-daemon assembleDebug
|
|
auto_commit_if_needed
|
|
log "Stopping any Gradle daemons just in case"
|
|
"$PROJECT_DIR/gradlew" --stop >/dev/null 2>&1 || true
|
|
}
|
|
|
|
main() {
|
|
require_tool curl
|
|
require_tool unzip
|
|
require_tool tar
|
|
|
|
ensure_dir "$TMP_DIR"
|
|
ensure_dir "$SDK_DIR"
|
|
|
|
ensure_jdk17
|
|
ensure_cmdline_tools
|
|
ensure_wrapper_jar
|
|
write_local_properties
|
|
ensure_sdk_packages
|
|
maybe_build "${1:-}"
|
|
|
|
log "Environment ready"
|
|
log "Project dir: $PROJECT_DIR"
|
|
log "JDK dir: $JDK_DIR"
|
|
log "SDK dir: $SDK_DIR"
|
|
log "To build without a background Gradle daemon: ./gradlew --no-daemon assembleDebug"
|
|
log "To setup and build in one step: bash ./SetupBuildEnvironment.sh --build"
|
|
}
|
|
|
|
main "$@" |