forked from aki/docker-aseprite-linux
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.
338 lines
15 KiB
Bash
Executable File
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"
|