1
0
aseprite-flatpak-builder/prepare_sources.sh
aki b0479bfbf4 feat: Implement Flatpak build system using flatpak-builder in Docker
This commit replaces the previous multi-distribution Docker build system with a unified Flatpak build process executed inside a Docker container. This is the intended final architecture for building Aseprite bundles.

**Key Changes:**

*   **Transition to Flatpak:** The core build logic now uses `flatpak-builder` and a Flatpak manifest (`com.aseprite.Aseprite.yaml`) to compile Aseprite and its dependencies against a standard Flatpak SDK runtime.
*   **Unified Docker Environment:** Replaced distribution-specific `Dockerfile.<distro>` files with a single `Dockerfile` that sets up a consistent environment containing `flatpak-builder` and the necessary Flatpak SDK.
*   **Simplified Makefile:** Removed OS detection logic and multi-distro targets. The Makefile now orchestrates:
    1.  Running `prepare_sources.sh` on the host (still responsible for reliable source fetching/syncing).
    2.  Building the single Flatpak builder Docker image.
    3.  Running `flatpak-builder` inside a *privileged* container (required for `flatpak-builder` sandboxing) to perform the actual build.
    4.  Running `flatpak build-bundle` inside the container.
    5.  Extracting the final `.flatpak` bundle from the container to `./target/aseprite.flatpak`.
*   **Updated .gitignore:** Added `build/`, `target/`, `*.flatpak`, and `*.log` to ignore Flatpak build directories, output bundles, and logs. Removed the old `dependencies` ignore pattern.
*   **Prepare Sources Update:** Modified `prepare_sources.sh` to explicitly initialize `depot_tools` on the host, as this is required before sources are copied into the Flatpak build environment for `gn` usage.
*   **Removal of Old Files:** Deleted `Dockerfile.<distro>`, `Dockerfile.debian`, `Dockerfile.fedora`, `Dockerfile.arch` (multi-distro Dockerfiles), and the original generic `Dockerfile` and `docker-compose.yml`.

**Rationale:**

This refactor moves to the planned final architecture. Building within a Flatpak SDK provides a highly consistent environment independent of the host Linux distribution. The output is a portable `.flatpak` bundle, simplifying distribution and runtime compatibility compared to dynamically linking against varied host libraries. While `prepare_sources.sh` on the host still handles the initial (and potentially rate-limited) source fetching, the subsequent build process is significantly standardized and more reliable.

This architecture is now the **forward-maintained** build method.
2025-05-06 17:02:01 +08:00

338 lines
15 KiB
Bash
Executable File

#!/bin/bash
# Exit immediately if a command exits with a non-zero status.
set -e
# --- Configuration ---
# Define repositories and their properties
# Format: "name|url|tag_or_branch|directory|has_submodules|is_tag"
REPOS=(
"depot_tools|https://chromium.googlesource.com/chromium/tools/depot_tools.git|origin/main|depot_tools|false|false"
"skia|https://github.com/aseprite/skia.git|m124-08a5439a6b|skia|false|true" # Skia uses git-sync-deps, not git submodules
"aseprite|https://github.com/aseprite/aseprite.git|v1.3.14-beta1|aseprite|true|true"
)
SRC_DIR="./src"
MAX_SYNC_RETRIES=${PREPARE_RETRIES:-2} # Default to 2 retries (3 total attempts) if PREPARE_RETRIES is not set
RETRY_DELAY=10 # Seconds to wait between retries
# --- Helper Functions (New Formatting) ---
print_usage() {
echo "Usage: $0 [--check-integrity] [-h|--help]"
echo ""
echo "Prepares source code repositories (depot_tools, Skia, Aseprite)."
echo ""
echo "Default behavior:"
echo " - Clones repositories if they don't exist."
echo " - If repositories exist, performs minimal updates (e.g., ensures Aseprite submodules are present)."
echo " - Errors out if cloning or minimal updates fail."
echo ""
echo "Options:"
echo " --check-integrity If repositories exist, performs a full integrity check and reset:"
echo " - Verifies repository integrity (git fsck)."
echo " - Fetches latest updates and tags."
echo " - Resets the repository to the specified tag/branch (discarding local changes)."
echo " - Updates and resets submodules to the state expected by the parent repository."
echo " - Errors out if directories are missing or if critical checks/resets fail."
echo " -h, --help Display this help message and exit."
}
_CURRENT_STEP=0
_TOTAL_STEPS=${#REPOS[@]} # Number of repositories
_TOTAL_STEPS=$((_TOTAL_STEPS + 2)) # +1 for initializing depot_tools, +1 for Skia sync
print_step() {
_CURRENT_STEP=$((_CURRENT_STEP + 1))
# Adjust total steps display if needed, though this simple count is often sufficient
echo -e "\n[\033[1;34mSTEP ${_CURRENT_STEP}/${_TOTAL_STEPS}\033[0m] $1..."
}
print_info() {
echo -e "[\033[0;32mINFO\033[0m] $1"
}
print_warning() {
echo -e "[\033[1;33mWARN\033[0m] $1"
}
print_success() {
echo -e "[\033[1;32mSUCCESS\033[0m] $1"
}
print_error() {
echo -e "[\033[1;31mERROR\033[0m] $1" >&2
}
print_process_start() {
echo -e "[\033[1mPREPARE\033[0m] $1"
}
print_process_end() {
echo -e "[\033[1mPREPARE\033[0m] $1"
}
# --- Argument Parsing ---
CHECK_INTEGRITY=false
while [[ "$#" -gt 0 ]]; do
case $1 in
--check-integrity) CHECK_INTEGRITY=true; shift ;;
-h|--help) print_usage; exit 0 ;;
*) print_error "Unknown parameter passed: $1"; print_usage; exit 1 ;;
esac
done
# --- Main Logic ---
print_process_start "Starting Source Preparation"
mkdir -p "$SRC_DIR"
cd "$SRC_DIR"
SRC_DIR_ABS=$(pwd) # Use absolute path for clarity inside script
cd .. # Go back to project root
print_info "Source directory: ${SRC_DIR_ABS}"
if [[ "$CHECK_INTEGRITY" == "true" ]]; then
print_info "Mode: Full Integrity Check and Reset (on existing directories)"
else
print_info "Mode: Default (Clone if missing, minimal update if exists)"
fi
# --- Process Repositories ---
DEPOT_TOOLS_DIR="" # Will be set when processing depot_tools
for repo_info in "${REPOS[@]}"; do
IFS='|' read -r name url target_ref dir has_submodules is_tag <<< "$repo_info"
REPO_DIR="${SRC_DIR_ABS}/${dir}"
# Set DEPOT_TOOLS_DIR for later use in PATH
if [[ "$name" == "depot_tools" ]]; then
DEPOT_TOOLS_DIR="$REPO_DIR"
fi
print_step "Processing ${name} (Target: ${target_ref})"
if [ -d "$REPO_DIR" ]; then
# Directory Exists
if [[ "$CHECK_INTEGRITY" == "true" ]]; then
# --- Check Integrity Logic ---
print_info "Verifying integrity of existing ${name} repository..."
if ! (cd "$REPO_DIR" && git fsck); then
print_warning "git fsck reported issues for ${name}, but continuing. Manual check recommended."
fi
print_info "Fetching updates for ${name}..."
fetch_args=("origin")
if [[ "$is_tag" == "true" ]]; then
fetch_args+=("--tags")
fi
if ! (cd "$REPO_DIR" && git fetch "${fetch_args[@]}"); then
print_error "Failed to fetch updates for ${name}. Check network or repository access."
exit 1
fi
needs_reset=false
if [[ "$is_tag" == "true" ]]; then
# Check against specific tag commit
CURRENT_COMMIT=$(cd "$REPO_DIR" && git rev-parse HEAD)
# Handle potential errors if tag doesn't exist locally yet after fetch
TARGET_COMMIT=$(cd "$REPO_DIR" && git rev-list -n 1 "${target_ref}" 2>/dev/null || echo "NOT_FOUND")
if [[ "$TARGET_COMMIT" == "NOT_FOUND" ]]; then
print_error "Target tag '${target_ref}' not found for ${name} after fetch."
exit 1
fi
if [[ "$CURRENT_COMMIT" != "$TARGET_COMMIT" ]]; then
needs_reset=true
print_info "${name} is not on the target commit for tag ${target_ref}."
fi
else
# Check if behind the target branch (e.g., origin/main for depot_tools)
# A simple way is to check if reset --hard changes HEAD
current_head=$(cd "$REPO_DIR" && git rev-parse HEAD)
target_head=$(cd "$REPO_DIR" && git rev-parse "${target_ref}")
if [[ "$current_head" != "$target_head" ]]; then
needs_reset=true
print_info "${name} is not on the target commit for branch ${target_ref}."
fi
fi
# Check for local modifications
MODIFIED=$(cd "$REPO_DIR" && git status --porcelain)
if [[ -n "$MODIFIED" ]]; then
needs_reset=true
print_info "${name} has local modifications."
fi
# Perform reset if needed
if [[ "$needs_reset" == "true" ]]; then
print_info "Resetting ${name} repository to target ${target_ref}..."
# Checkout first, especially important for tags, suppresses detached HEAD advice
if ! (cd "$REPO_DIR" && git checkout "${target_ref}"); then
print_error "Failed to checkout ${target_ref} for ${name}."
exit 1
fi
# Reset hard to ensure clean state matching the target ref
if ! (cd "$REPO_DIR" && git reset --hard "${target_ref}"); then
print_error "Failed to reset ${name} to ${target_ref}."
exit 1
fi
print_info "${name} repository reset successfully."
else
print_info "${name} repository is already on target ${target_ref} and clean."
fi
# Handle Submodules (if applicable)
if [[ "$has_submodules" == "true" ]]; then
print_info "Ensuring ${name} submodules are initialized and updated..."
if ! (cd "$REPO_DIR" && git submodule update --init --recursive); then
print_error "Failed to update submodules for ${name}. Check network or repository access."
exit 1
fi
print_info "Submodule update command completed."
print_info "Checking ${name} submodule status and internal state..."
# Check 1: Overall status (uninitialized, wrong commit). Append '|| true' to prevent grep exit code 1 from stopping the script with set -e.
SUBMODULE_STATUS_ISSUES=$(cd "$REPO_DIR" && git submodule status | grep -v '^ ' || true)
# Check 2: Internal state (modified content, untracked files within submodules). Append '|| true' for robustness.
# Use --quiet to suppress "Entering 'path'" messages. Redirect stderr in case of errors within foreach.
SUBMODULE_INTERNAL_CHANGES=$(cd "$REPO_DIR" && git submodule foreach --quiet 'git status --porcelain' 2>&1 || true)
if [[ -n "$SUBMODULE_STATUS_ISSUES" || -n "$SUBMODULE_INTERNAL_CHANGES" ]]; then
if [[ -n "$SUBMODULE_STATUS_ISSUES" ]]; then
print_info "Detected submodules not initialized or on wrong commit:"
echo "$SUBMODULE_STATUS_ISSUES" # Show which ones have status issues
fi
if [[ -n "$SUBMODULE_INTERNAL_CHANGES" ]]; then
# We don't print the full output of internal changes as it can be verbose,
# just knowing *that* there are changes is enough to trigger the reset.
print_info "Detected submodules with internal changes (modified/untracked files)."
fi
print_info "Resetting submodules for ${name} to clean state..."
# --- Reset Logic ---
if ! (cd "$REPO_DIR" && git submodule foreach --quiet git reset --hard HEAD); then
print_error "Failed to reset submodules for ${name}. Manual check required in ${REPO_DIR}."
exit 1
fi
print_info "Running integrity check on reset submodules..."
# fsck after reset is less critical, treat as warning
if ! (cd "$REPO_DIR" && git submodule foreach --quiet git fsck); then
print_warning "git fsck reported issues for some submodules in ${name} after reset, but continuing."
fi
print_info "Submodules reset successfully."
# --- End Reset Logic ---
else
print_info "${name} submodules already in correct state."
fi
fi
print_success "Integrity check and state validation complete for ${name}."
else
# --- Default Minimal Update Logic ---
print_info "${name} directory exists. Performing minimal update..."
# Only Aseprite needs minimal submodule update in default mode currently
if [[ "$name" == "aseprite" && "$has_submodules" == "true" ]]; then
print_info "Ensuring ${name} submodules are initialized and updated..."
if ! (cd "$REPO_DIR" && git submodule update --init --recursive); then
print_error "Failed to perform minimal submodule update for ${name}. Use --check-integrity or check manually."
exit 1
fi
print_success "Minimal submodule update for ${name} complete."
else
print_info "No minimal update action needed for ${name}."
fi
fi
else
# Directory Does Not Exist
if [[ "$CHECK_INTEGRITY" == "true" ]]; then
print_error "Directory ${REPO_DIR} for ${name} is missing. Cannot perform --check-integrity."
exit 1
else
# --- Clone Logic ---
print_info "Cloning ${name} from ${url} (Target: ${target_ref})..."
clone_args=("--depth" "1")
if [[ "$is_tag" == "true" || "$target_ref" != "origin/main" ]]; then
# For tags or specific branches, use --branch
clone_args+=("--branch" "${target_ref}")
fi
if [[ "$has_submodules" == "true" ]]; then
clone_args+=("--recursive")
fi
if ! git clone "${clone_args[@]}" "${url}" "${REPO_DIR}"; then
print_error "Failed to clone ${name} from ${url}."
exit 1
fi
print_success "${name} cloned successfully."
fi
fi
done
# Initialize depot_tools after all repositories are processed
print_step "Initializing depot_tools"
if [[ -n "$DEPOT_TOOLS_DIR" && -d "$DEPOT_TOOLS_DIR" ]]; then
print_info "Running depot_tools initialization..."
# Add depot_tools to PATH for proper initialization
export PATH="${DEPOT_TOOLS_DIR}:${PATH}"
# Check if initialization was already done
if [[ ! -f "${DEPOT_TOOLS_DIR}/python3_bin_reldir.txt" ]]; then
# Run ensure_bootstrap to initialize depot_tools
if (cd "$DEPOT_TOOLS_DIR" && ./ensure_bootstrap); then
print_success "depot_tools initialized successfully."
else
print_error "Failed to initialize depot_tools. Build may fail."
exit 1
fi
else
print_info "depot_tools already initialized (python3_bin_reldir.txt exists)."
fi
else
print_error "depot_tools directory not found or not processed correctly. Cannot initialize."
exit 1
fi
# Sync Skia dependencies locally with retry logic (after Skia repo is processed)
# Find Skia dir again (could be improved by storing dirs in an associative array if using Bash 4+)
SKIA_DIR=""
for repo_info in "${REPOS[@]}"; do
IFS='|' read -r name url target_ref dir has_submodules is_tag <<< "$repo_info"
if [[ "$name" == "skia" ]]; then
SKIA_DIR="${SRC_DIR_ABS}/${dir}"
break
fi
done
if [[ -z "$SKIA_DIR" || ! -d "$SKIA_DIR" ]]; then
print_error "Skia directory not found or not processed correctly. Cannot sync dependencies."
exit 1
fi
if [[ -z "$DEPOT_TOOLS_DIR" || ! -d "$DEPOT_TOOLS_DIR" ]]; then
print_error "Depot tools directory not found or not processed correctly. Cannot sync Skia dependencies."
exit 1
fi
print_step "Syncing Skia Dependencies"
print_info "Attempting to run git-sync-deps in ${SKIA_DIR} (up to $((MAX_SYNC_RETRIES + 1)) attempts)..."
# Ensure depot_tools is in PATH for git-sync-deps internal calls if needed
export PATH="${DEPOT_TOOLS_DIR}:${PATH}"
sync_success=false
for (( attempt=0; attempt<=MAX_SYNC_RETRIES; attempt++ )); do
if (cd "$SKIA_DIR" && python3 tools/git-sync-deps); then
sync_success=true
print_success "Skia dependencies synced successfully on attempt $((attempt + 1))."
break
else
exit_code=$?
if [[ $attempt -lt $MAX_SYNC_RETRIES ]]; then
print_warning "git-sync-deps failed on attempt $((attempt + 1)) with exit code ${exit_code}. Retrying in ${RETRY_DELAY} seconds..."
sleep $RETRY_DELAY
else
print_error "git-sync-deps failed after $((attempt + 1)) attempts with exit code ${exit_code}. Please check network connection, rate limits, or Skia repository state."
# Consider adding advice to run with --check-integrity if the repo exists but might be broken.
exit 1
fi
fi
done
print_process_end "Source Preparation Complete"