From b0479bfbf4155c39679f88f0c9e103e70038f9af Mon Sep 17 00:00:00 2001 From: aki Date: Tue, 6 May 2025 17:02:01 +0800 Subject: [PATCH] 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.` 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.`, `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. --- .gitignore | 5 +- Dockerfile | 42 ++++ Makefile | 143 ++++++------ README.md | 208 ++++++----------- com.aseprite.Aseprite.yaml | 196 ++++++++++++++++ diff.txt | 448 ------------------------------------- prepare_sources.sh | 27 ++- 7 files changed, 403 insertions(+), 666 deletions(-) create mode 100644 Dockerfile create mode 100644 com.aseprite.Aseprite.yaml delete mode 100644 diff.txt diff --git a/.gitignore b/.gitignore index 8e4a455..e8fe342 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .idea src/ -target/ \ No newline at end of file +target/ +build/ +*.flatpak +*.log \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4e3bf8c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,42 @@ +# Use Debian 12 Slim as the base image +FROM debian:12-slim + +# Set environment variables for non-interactive install +ENV DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC + +# Install necessary tools: Flatpak, builder, git, etc. +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + flatpak \ + flatpak-builder \ + git \ + curl \ + unzip \ + ca-certificates \ + elfutils \ + # Build dependencies for system libraries + build-essential \ + ninja-build \ + pkg-config \ + python3 \ + # Clean up apt cache + && rm -rf /var/lib/apt/lists/* + +# Add Flathub remote and install the Freedesktop SDK and Platform +# Using 23.08 version as specified in the manifest +RUN flatpak remote-add --system --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo && \ + flatpak install --system --noninteractive flathub org.freedesktop.Sdk//23.08 && \ + flatpak install --system --noninteractive flathub org.freedesktop.Platform//23.08 + +# Set working directory for the build process +WORKDIR /build + +# Create cache-friendly directory structure +# This creates a clear layer separation for better Docker/Flatpak caching +RUN mkdir -p /sources /build/cache /build/build-dir /build/repo + +# Copy the Flatpak manifest into the build directory +COPY com.aseprite.Aseprite.yaml . + +# Default command for interactive use +CMD ["bash"] diff --git a/Makefile b/Makefile index 0984390..b439ad4 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,13 @@ -# --- OS Detection --- -# Attempt to detect host OS family. Fallback to 'arch'. -# This logic checks /etc/os-release for ID or ID_LIKE fields. -# Use $(strip ...) to remove potential leading/trailing whitespace from shell output. -DETECTED_DISTRO := $(strip $(shell \ - if [ -f /etc/os-release ]; then \ - . /etc/os-release; \ - if echo "$$ID$$ID_LIKE" | grep -q -e arch; then \ - echo arch; \ - elif echo "$$ID$$ID_LIKE" | grep -q -e debian -e ubuntu; then \ - echo debian; \ - elif echo "$$ID$$ID_LIKE" | grep -q -e fedora; then \ - echo fedora; \ - else \ - echo arch; \ - fi; \ - else \ - echo arch; \ - fi \ -)) - -# Set default TARGET_DISTRO based on detection, allowing user override -TARGET_DISTRO ?= $(DETECTED_DISTRO) - # --- Configuration --- -IMAGE_NAME := docker-aseprite-${TARGET_DISTRO} -DOCKERFILE := Dockerfile.${TARGET_DISTRO} -OUTPUT_DIR := ${PWD}/output -TARGET_DIR_IN_IMAGE := /target/aseprite/build/bin +# Uses a single Dockerfile with flatpak-builder inside +IMAGE_NAME := aseprite-flatpak-builder +DOCKERFILE := Dockerfile +TARGET_DIR := ${PWD}/target +OUTPUT_FILE := ${TARGET_DIR}/aseprite.flatpak +BUILD_DIR := ${PWD}/build +SRC_DIR := ${PWD}/src +FLATPAK_STATE_DIR := ${PWD}/flatpak-state +FLATPAK_CACHE_DIR := ${PWD}/flatpak-cache # --- Main Targets --- # Default target @@ -36,59 +17,79 @@ all: build # Use .PHONY to ensure it always runs if called directly .PHONY: prepare-sources prepare-sources: - @echo "--- Preparing sources ---" + @echo "--- Preparing sources using prepare_sources.sh ---" @./prepare_sources.sh -# Build the Docker image for the target distribution -# Depends on sources being ready (though prepare-sources isn't a file dependency) +# Build the Docker image which runs flatpak-builder inside +# Implicitly depends on sources being ready after prepare-sources runs +.PHONY: build-image build-image: -# --- DEBUGGING --- - @echo "DEBUG: TARGET_DISTRO='${TARGET_DISTRO}'" - @echo "DEBUG: DOCKERFILE='${DOCKERFILE}'" -# --- END DEBUGGING --- -ifeq (,$(wildcard ${DOCKERFILE})) - $(error Dockerfile for target distribution '${TARGET_DISTRO}' (${DOCKERFILE}) not found) -endif - @echo "--- Building Docker image (${IMAGE_NAME}) using ${DOCKERFILE} ---" + @echo "--- Building Aseprite Flatpak builder image (${IMAGE_NAME}) ---" @docker build -t ${IMAGE_NAME} -f ${DOCKERFILE} . -# Extract the compiled binary from the image -# Depends on the image being built -.PHONY: extract-binary -extract-binary: build-image - @echo "--- Extracting Aseprite binary from ${IMAGE_NAME} ---" - @mkdir -p ${OUTPUT_DIR}/bin - @CONTAINER_ID=$$(docker create ${IMAGE_NAME}) && \ - docker cp "$${CONTAINER_ID}:${TARGET_DIR_IN_IMAGE}/." "${OUTPUT_DIR}/bin/" && \ - docker rm "$${CONTAINER_ID}" > /dev/null - @echo "Aseprite binary extracted to ${OUTPUT_DIR}/bin" +# Run flatpak-builder inside a privileged container +# Create persistent cache directories to improve caching +.PHONY: run-flatpak-builder +run-flatpak-builder: + @echo "--- Running flatpak-builder inside container ---" + @mkdir -p ${BUILD_DIR} ${FLATPAK_STATE_DIR} ${FLATPAK_CACHE_DIR} + @docker run \ + --rm \ + --privileged \ + --device /dev/fuse \ + -v ${SRC_DIR}:/sources:ro \ + -v ${BUILD_DIR}:/build \ + -v ${FLATPAK_STATE_DIR}:/root/.local/share/flatpak-builder \ + -v ${FLATPAK_CACHE_DIR}:/root/.cache/flatpak-builder \ + -v ${PWD}/com.aseprite.Aseprite.yaml:/build/com.aseprite.Aseprite.yaml:ro \ + -w /build \ + ${IMAGE_NAME} \ + sh -c "flatpak-builder --disable-rofiles-fuse --state-dir=/root/.local/share/flatpak-builder --ccache --force-clean build-dir com.aseprite.Aseprite.yaml --repo=repo" -# Main build target: Prepare sources, build image, extract binary for selected distro +# Create the Flatpak bundle from the repo built in the previous step +.PHONY: create-bundle +create-bundle: run-flatpak-builder + @echo "--- Creating Flatpak bundle ---" + @docker run \ + --rm \ + --privileged \ + --device /dev/fuse \ + -v ${BUILD_DIR}:/build \ + -w /build \ + ${IMAGE_NAME} \ + sh -c "flatpak build-bundle repo aseprite.flatpak com.aseprite.Aseprite" + +# Copy the final bundle from the intermediate directory to the output directory +.PHONY: extract-flatpak +extract-flatpak: create-bundle + @echo "--- Copying Flatpak bundle to target directory ---" + @mkdir -p ${TARGET_DIR} + @cp ${BUILD_DIR}/aseprite.flatpak ${OUTPUT_FILE} + @echo "Aseprite Flatpak bundle is in ${OUTPUT_FILE}" + +# Prepare build target: Prepare sources, build Docker image +.PHONY: prepare +prepare: prepare-sources build-image + @echo "--- Prepare complete ---" + +# Main build target: Run builder, create bundle, extract .PHONY: build -build: prepare-sources build-image extract-binary - @echo "--- Build complete for ${TARGET_DISTRO} ---" - @echo "Aseprite binary is in ${OUTPUT_DIR}/bin" - -# --- Specific Distribution Targets --- -.PHONY: build-arch build-debian build-fedora - -# Build for Arch Linux (default if not detected otherwise) -build-arch: - $(MAKE) build TARGET_DISTRO=arch - -# Build for Debian/Ubuntu based systems -build-debian: - $(MAKE) build TARGET_DISTRO=debian - -# Build for Fedora based systems -build-fedora: - $(MAKE) build TARGET_DISTRO=fedora +build: run-flatpak-builder create-bundle extract-flatpak + @echo "--- Build complete ---" # --- Utility Targets --- -# Clean up downloaded sources and output directory +# Clean up the output and intermediate build directories .PHONY: clean clean: @echo "--- Cleaning up ---" - @rm -rf ./src - @rm -rf ${OUTPUT_DIR} + @rm -rf ${TARGET_DIR} + @rm -rf ${BUILD_DIR} @echo "Cleanup complete." + +# Deep clean - removes all caches as well +.PHONY: deep-clean +deep-clean: clean + @echo "--- Deep cleaning (including caches) ---" + @rm -rf ${FLATPAK_STATE_DIR} + @rm -rf ${FLATPAK_CACHE_DIR} + @echo "Deep cleanup complete." diff --git a/README.md b/README.md index c9e694c..441be97 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,20 @@ -# Docker Aseprite Builder +# Docker Aseprite Flatpak Builder -This repository provides a Docker-based environment to compile Aseprite easily. It aims to improve compatibility with your host Linux system by building Aseprite within a Docker container that matches your distribution family (Arch, Debian/Ubuntu, or Fedora). +This repository provides a Docker-based environment to compile Aseprite into a portable Flatpak bundle. It uses `flatpak-builder` inside a containerized environment for consistency and leverages a robust host-side script to prepare sources reliably. ## Requirements -* Docker +* Docker (BuildKit backend enabled, which is default in modern versions) * Make * Git * Curl -* Access to `/etc/os-release` on the host system (for the optional auto-detection feature). +* Host kernel with `fuse` module loaded (common on most desktop Linux systems). +* Sufficient disk space and RAM for source code and compilation. * **CPU with AVX support:** The compiled binary requires a CPU supporting the AVX instruction set (generally CPUs from 2011/Sandy Bridge/Bulldozer onwards). +> [!IMPORTANT] +> The Docker build process requires elevated privileges (`--privileged`) to allow `flatpak-builder`'s sandboxing (bubblewrap) to function correctly. The `Makefile` handles adding this flag automatically. Ensure your environment allows privileged Docker builds. + ## Quick Start 1. **Clone:** @@ -26,127 +30,67 @@ This repository provides a Docker-based environment to compile Aseprite easily. make build ``` -3. **Run:** Find the compiled binary in `./output/bin/aseprite`. +3. **Run:** Find the compiled Flatpak bundle in `./output/aseprite.flatpak`. You can install it using: -> [!WARNING] -> **Compatibility Notice:** The compiled binary is dynamically linked. While this builder tries to match your OS family, minor differences between the build environment and your host system's libraries *can* still cause runtime errors. Please see the [Troubleshooting & Compatibility](#troubleshooting--compatibility) section for details. + ```bash + flatpak install ./output/aseprite.flatpak + ``` ## Build Process Explained -### OS Family Auto-Detection - -When you run `make build`, the `Makefile` attempts to detect your host operating system family by reading `/etc/os-release`. It looks for identifiers to classify your system as: - -* **Arch-based:** (`ID` or `ID_LIKE` contains `arch`) -> Uses `Dockerfile.arch` (See file for base image version) -* **Debian/Ubuntu-based:** (`ID` or `ID_LIKE` contains `debian` or `ubuntu`) -> Uses `Dockerfile.debian` (See file for base image version) -* **Fedora-based:** (`ID` or `ID_LIKE` contains `fedora`) -> Uses `Dockerfile.fedora` (See file for base image version) - -**Default:** If detection fails or your OS family isn't recognized, it defaults to using the **Arch Linux** build environment. - -### Build Steps - -The `make build` command performs the following steps automatically: +The `make build` command performs the following steps: 1. **Prepare Sources (via `./prepare_sources.sh`):** + * This script runs on your **host machine** first to reliably acquire and prepare the source code. * Clones `depot_tools`, Skia, and Aseprite source code into the `./src` directory if they don't exist. See the script for specific versions. - * Runs Skia's `git-sync-deps` command locally to download Skia's internal build dependencies (like the `gn` tool). - * **Note on Skia Sync:** The `git-sync-deps` step can sometimes fail due to network issues or rate limiting (HTTP 429 errors) from Google's servers. The script automatically retries this step up to 3 times (1 initial attempt + 2 retries) with a 10-second delay between attempts. You can control the number of *retries* by setting the `PREPARE_RETRIES` environment variable when running `make` (e.g., `PREPARE_RETRIES=5 make build`). -2. **Build Docker Image:** Builds a Docker image (e.g., `docker-aseprite-arch`) using the selected `Dockerfile.`. Inside the container: - * Necessary build tools and development libraries are installed using the distribution's package manager (`pacman`, `apt`, `dnf`). - * `depot_tools` is initialized. - * Skia is compiled, configured to use the system-provided libraries (avoids network issues and improves compatibility). - * Aseprite is compiled, linking against the compiled Skia and other system libraries. -3. **Extract Binary:** Copies the compiled `aseprite` binary and its `data` directory from the container to `./output/bin` on your host machine. + * Runs Skia's `git-sync-deps` command locally with **automatic retries** (default: 3 attempts, configurable via `PREPARE_RETRIES`) to download Skia's internal build dependencies (like the `gn` tool) and mitigate failures from network issues or rate limiting. + * Includes an **`--check-integrity`** flag for advanced validation and resetting of source repositories (see Advanced Usage). + * This step isolates the often failure-prone network operations and source state management before the main build begins. +2. **Build Docker Image (using `Dockerfile`):** + * Builds a single Docker image (`aseprite-flatpak-builder`) based on Debian Slim. + * Installs `flatpak`, `flatpak-builder`, and the required Freedesktop Platform/SDK (e.g., 23.08). + * **Copies** the prepared sources from `./src` on the host into `/sources` inside the container. This leverages Docker's layer caching for the source code. + * Copies the Flatpak manifest `com.aseprite.Aseprite.yaml`. + * Runs `flatpak-builder` inside the container using the manifest and the copied sources. This compiles Skia and Aseprite within the consistent Flatpak SDK environment. + * Bundles the result into an `aseprite.flatpak` file inside the container. + * **Note:** This step runs with `--privileged` to allow `flatpak-builder`'s sandboxing mechanisms to work. +3. **Extract Flatpak Bundle:** Copies the final `aseprite.flatpak` file from the container's `/output` directory to `./output` on your host machine. -### Overriding Auto-Detection - -If the auto-detection chooses the wrong target, or you want to build for a different distribution family, you can override it: - -```bash -# Force build using Debian environment -make build TARGET_DISTRO=debian - -# Force build using Fedora environment -make build TARGET_DISTRO=fedora - -# Force build using Arch environment -make build TARGET_DISTRO=arch -``` - -Alternatively, use the specific make targets: - -```bash -make build-debian -make build-fedora -make build-arch -``` - -## Troubleshooting & Compatibility - -### Detailed Compatibility Explanation - -Aseprite, like many Linux applications, relies on shared libraries (`.so` files) provided by the operating system (e.g., `libpng.so`, `libfreetype.so`, `libX11.so`). This process is called dynamic linking. - -* **Goal:** This builder aims to improve compatibility by compiling Aseprite inside a Docker container using the same OS *family* as your host system. This means Aseprite links against libraries (e.g., Fedora libraries) that are likely similar to those on your host (e.g., your Fedora installation). -* **The Challenge (Versioning):** Linux distributions and their libraries are constantly updated. The Docker image used for building (e.g., the one specified in `Dockerfile.fedora`) captures a snapshot of libraries at a specific point in time. Your host system might have slightly older or newer *minor versions* of these libraries (e.g., `libpng.so.1.6.40` in the container vs. `libpng.so.1.6.43` on the host). -* **ABI Stability:** Library developers usually try very hard to maintain Application Binary Interface (ABI) stability across minor versions. This means newer libraries can often run programs compiled against slightly older versions (backward compatibility). -* **Potential Issues:** - * **Build Newer, Run Older (More Risky):** If the build container has a *newer* library version than your older host system, and Aseprite uses a function only present in that newer library, you will likely get runtime errors ("undefined symbol") on the host. - * **Build Older, Run Newer (Usually Safer):** If the build container used an older library version than your newer host system, it's *more likely* to work due to backward compatibility efforts. - * **Subtle Breaks:** Very rarely, even minor version changes can introduce subtle ABI breaks causing crashes. -* **Conclusion:** While building for the correct OS family significantly reduces problems, minor version mismatches between the build environment and your host *can* still cause issues. 100% compatibility is hard to guarantee with dynamic linking. - -### Adapting Dockerfiles for Older Systems (Advanced / Use with Caution) - -If you are using an older version of a distribution and encounter runtime errors *after* building with the corresponding family target (e.g., `make build TARGET_DISTRO=fedora`), you *could* try modifying the `Dockerfile.`: - -1. **Identify the correct Dockerfile** (e.g., `Dockerfile.fedora`). -2. **Change the `FROM` line:** Update the base image tag to an older version that matches your system (e.g., change the tag in `FROM fedora:...` to an older one like `fedora:39`). Find available tags on Docker Hub. -3. **Adjust Dependencies:** Older distributions might have different package names or versions. You may need to adjust the `dnf install` (or `apt`, `pacman`) commands in the Dockerfile if the build fails due to missing packages. -4. **Rebuild:** Run `make build TARGET_DISTRO=fedora` again. - -**Disclaimer:** This is an advanced procedure. It might not work, may require significant troubleshooting of package dependencies in the Dockerfile, and is generally less recommended than upgrading your host operating system. - -### Common Runtime Errors - -* **`./aseprite: error while loading shared libraries: libfoo.so.X: cannot open shared object file: No such file or directory`**: Your host system is missing the required library `libfoo.so.X`. Install the package that provides this library using your system's package manager (e.g., `sudo dnf install foo`, `sudo apt install libfooX`, `sudo pacman -S foo`). Common examples needed by Aseprite or its dependencies include `libXcursor`, `libXi`, `freetype`, `harfbuzz`, `libstdc++`. -* **`./aseprite: ./aseprite: symbol lookup error: ./aseprite: undefined symbol: some_function_name`**: This usually indicates an ABI incompatibility, often because the library version on your host is older than the one used during the build and is missing `some_function_name`. See the compatibility notes above. Upgrading your host system or trying the advanced Dockerfile adaptation might be necessary. -* **`./aseprite: Illegal instruction`**: This typically means the compiled binary is trying to use CPU instructions not supported by your processor. This build requires a CPU with **AVX** support. +## Troubleshooting ### Build Failures * **Resource Exhaustion:** Compiling Skia and Aseprite requires significant RAM and disk space. Ensure your system (and Docker) has adequate resources allocated. -* **Network Issues / Rate Limiting:** `prepare_sources.sh` needs to clone repositories and sync Skia's dependencies (`git-sync-deps`), which involves many network requests. +* **Network Issues / Rate Limiting (during `prepare_sources.sh`):** * Ensure you have a stable internet connection. Check for proxy issues if applicable. - * The Skia sync step might hit rate limits (HTTP 429 errors) from Google's servers. The script automatically retries this step (default: 3 total attempts). If it consistently fails, wait a while before trying again, or increase the number of retries via the `PREPARE_RETRIES` environment variable (e.g., `PREPARE_RETRIES=5 make build`). -* **Package Not Found (Dockerfile):** If a `dnf`, `apt`, or `pacman` command fails inside the Docker build, a package name might be incorrect for the chosen base image version, or the distribution's repositories might be temporarily unavailable. -* **Compilation Errors:** Check the detailed build log output from Docker for C++/CMake errors. These could indicate issues with the source code versions, compiler compatibility, or missing dependencies. + * The Skia sync step (`git-sync-deps`) might hit rate limits (HTTP 429 errors) from Google's servers. The script automatically retries (default: 3 total attempts). If it consistently fails, wait a while before trying again, or increase the number of retries via the `PREPARE_RETRIES` environment variable (e.g., `PREPARE_RETRIES=5 make build`). +* **`flatpak-builder` Errors (during Docker build):** + * **Manifest Errors:** Check `com.aseprite.Aseprite.yaml` for syntax errors or incorrect paths/commands. + * **SDK Issues:** Ensure the SDK version specified in the manifest and Dockerfile is available and installs correctly. + * **Sandbox/Bubblewrap Errors (e.g., FUSE issues):** + * Verify the `docker build` command is running with `--privileged` (handled by the Makefile). + * Ensure the `fuse` kernel module is loaded on your **host system** (`lsmod | grep fuse`). If not, try `sudo modprobe fuse`. + * Check Docker/BuildKit logs for specific bubblewrap (`bwrap`) errors. + * **Compilation Errors:** Check the detailed build log output from Docker (during the `flatpak-builder` step) for C++/CMake/Ninja errors. These could indicate issues with the source code versions, compiler compatibility, or missing dependencies within the Flatpak SDK. -### Auto-Detection Failure +### Runtime Errors (After Installing `.flatpak`) -If the Makefile defaults to Arch but you use a different distribution, it means the detection logic couldn't identify your OS from `/etc/os-release`. Use the `TARGET_DISTRO` variable or specific make targets (`make build-debian`, `make build-fedora`) to select the correct environment. +* **`Illegal instruction`**: This typically means the compiled binary is trying to use CPU instructions not supported by your processor. This build requires a CPU with **AVX** support. +* **Missing Portals / File Dialog Issues:** Ensure you have the necessary Flatpak portals installed on your host system (e.g., `xdg-desktop-portal`, `xdg-desktop-portal-gtk` or `-kde`, etc.) for features like file choosers to work correctly. +* **Permission Issues:** If Aseprite cannot save files or access expected locations, review the `finish-args` in `com.aseprite.Aseprite.yaml`. The current `--filesystem=home` is broad; more restrictive permissions might require specific portal interactions. ## Advanced Usage -The `make build` command automatically runs `./prepare_sources.sh` without any special flags. If you need to control the source preparation step (e.g., force a re-download or check integrity), you can run the script manually *before* running `make build`, or pass flags through `make`. +The `make build` command automatically runs `./prepare_sources.sh` without any special flags. If you need to control the source preparation step (e.g., force a check integrity and reset), you can run the script manually *before* running `make build`. ### Running `prepare_sources.sh` Manually -You can run the script directly with its flags: - -* **Force Re-download Sources:** Deletes `./src` and clones fresh. Useful for corruption or persistent sync issues. - - ```bash - ./prepare_sources.sh --force-redownload - make build # Now run the build with fresh sources - ``` - -* **Check Source Integrity:** Runs `git fsck` on existing source repos. Does not re-download unless corruption is found and you *then* run with `--force-redownload`. +* **Check Source Integrity & Reset:** Runs `git fsck`, fetches updates, checks out the exact target commit/tag, and resets submodules. **Warning:** This performs potentially network-intensive operations and modifies local source directories to match the expected state. ```bash ./prepare_sources.sh --check-integrity - # If no errors, proceed with make build - # If errors, consider running with --force-redownload + # If successful, proceed with make build + make build ``` * **Set Sync Retries:** Control the number of retries for the Skia `git-sync-deps` step (default is 2 retries, total 3 attempts). @@ -158,69 +102,45 @@ You can run the script directly with its flags: ### Passing Options via `make` -While you can run the script manually first, you can also influence the `prepare_sources.sh` step when running `make build` by passing variables: - * **Set Sync Retries via `make`:** ```bash PREPARE_RETRIES=5 make build ``` -* **Force Re-download via `make` (More Complex):** The `Makefile` doesn't directly support passing flags like `--force-redownload` to the script. The recommended way is to run the script manually first as shown above, or to run `make clean` followed by `make build`. +* **Force Source Reset via `make`:** The recommended way to ensure sources are reset and prepared fresh is to run `make clean` followed by `make build`. ```bash - make clean && make build # Clean first, then build (includes fresh source prep) + make clean && make build # Clean output, then build (includes fresh source prep) ``` ### Clean Up -To remove the downloaded sources (`./src`) and the build output (`./output`): +To remove the build output (`./output`): ```bash make clean ``` +Note: This does *not* remove the downloaded sources in `./src`. To remove those as well, manually delete the `./src` directory or run `rm -rf ./src`. -## Notes +## Architecture Notes -* **Source Versions:** See `prepare_sources.sh` for the specific Git tags of Skia and Aseprite that are cloned. -* **System Libraries:** Skia is built using system libraries from the container to improve compatibility and avoid build-time network issues. -* **Major Build System Refactoring (Improved Reliability & Path to Portability)** +This project uses a hybrid approach combining a robust host-side source preparation script with a containerized Flatpak build process: - This project's build system has been **significantly refactored**. The primary goals were to improve **build reliability**, particularly concerning dependency fetching and source state management, and to establish an intermediate step towards a fully portable Flatpak-based build. +1. **Host-Side Source Prep (`prepare_sources.sh`):** Handles the potentially fragile steps of cloning, syncing dependencies (with retries), and ensuring the correct state of source code repositories locally in `./src`. +2. **Docker Build (`Dockerfile`):** + * Creates a consistent build environment using Debian and the standard Flatpak SDK. + * Copies the locally prepared sources into the container, leveraging Docker's layer caching. + * Runs `flatpak-builder` using `com.aseprite.Aseprite.yaml` to compile Aseprite against the SDK libraries and the copied sources. Requires `--privileged` for sandboxing. +3. **Flatpak Manifest (`com.aseprite.Aseprite.yaml`):** Defines the application metadata, permissions, and the sequence of build steps (depot\_tools setup, Skia compile, Aseprite compile, metadata installation) executed by `flatpak-builder`. +4. **Makefile:** Orchestrates the process: `prepare_sources.sh` -> `docker build --privileged` -> extract `.flatpak`. - **Problems with the Old System:** - - 1. **Build Fragility due to Dependency Fetching & State:** The previous method was highly susceptible to failures caused by **network interruptions** and **rate limiting** during source/sub-dependency acquisition (especially Skia's `git-sync-deps`). Inconsistent or incorrect local source states could also lead to build failures, often deep into a long process. - 2. **Complexity:** Managing the full source compilation for all dependencies was complex. - - **The New Architecture: Separating Concerns** - - The refactored system uses a multi-stage approach: - - 1. **Dedicated Host-Side Source Preparation (`prepare_sources.sh`):** This script now handles the critical and often problematic steps of acquiring and ensuring the correct state of the sources *on the host machine before* the Docker build begins. Key features: - * Clones/updates primary source repositories (`depot_tools`, Skia, Aseprite). - * Runs Skia's `git-sync-deps` with **automatic retries** (default: 3 attempts, configurable via `PREPARE_RETRIES`) to mitigate failures from rate limiting. - * Includes an **`--check-integrity`** flag. **Note:** This is more than a passive check; it performs **aggressive actions** like fetching from remotes, checking out specific tags/commits, and resetting the local repository state to forcefully ensure the sources precisely match what the build expects. This can involve significant network activity and local changes. - * This isolation aims to handle the most failure-prone operations (network fetches, state management) upfront and more robustly. - 2. **Distribution-Specific Dockerfiles (Interim Build Environment):** Once sources are prepared and verified, the build continues inside a Docker container specific to a Linux family (`Dockerfile.arch`, `.debian`, `.fedora`). These use native package managers (`pacman`, `apt`, `dnf`) to install *common* pre-built development libraries, simplifying the compilation phase within Docker. The `Makefile` selects the appropriate Dockerfile via OS detection or the `TARGET_DISTRO` variable. - 3. **Clear Makefile Orchestration:** Manages the sequence: Run `prepare_sources.sh`, build the selected Docker image, extract the final binary to `./output/bin`. - - **How to Use the New System:** - - * **Automatic:** `make build` (attempts OS detection). - * **Manual Override:** `make build TARGET_DISTRO=debian|fedora|arch` or `make build-debian|build-fedora|build-arch`. - * **Force Source Reset:** If builds fail due to source issues, consider running `make clean` followed by `make build`, or potentially running `./prepare_sources.sh --force-redownload` manually before `make build` (see Advanced Usage). - - **Important Caveats & The Path Forward: Flatpak** - - * **Source Preparation is Key:** Build success heavily relies on the `prepare_sources.sh` script completing successfully, including potentially intensive network operations (especially with `--check-integrity` or during initial clones/syncs). Rate limits or network problems during this phase can still block the build. - * **Multi-Distro is Intermediate:** This approach is a step to improve reliability over the previous full-source build, but maintaining separate Dockerfiles is temporary. - * **The Future is Flatpak (Portability & Consistency):** The **definitive, long-term, and forward-maintained solution** is migrating to **Flatpak tooling (`flatpak-builder`)**. This provides: - * A **unified build environment** using standard runtimes, independent of the host OS. - * A **portable `.flatpak` bundle** output for consistent execution across Linux distributions. - * **Simplified Maintenance:** Eliminates the need for OS detection and multiple distribution-specific Dockerfiles for the build environment itself. - Flatpak standardizes the build environment and packaging *after* the initial source preparation step is complete. **Development effort will focus on the Flatpak migration.** +This architecture aims for: +* **Reliability:** By handling source fetching/syncing with retries outside the main build. +* **Consistency:** By using the standard Flatpak SDK inside Docker. +* **Portability:** By producing a `.flatpak` bundle that should run across different Linux distributions. +* **Maintainability:** By consolidating the build logic into a single Dockerfile and Flatpak manifest. ## License -[MIT License](LICENSE) \ No newline at end of file +[MIT License](LICENSE) diff --git a/com.aseprite.Aseprite.yaml b/com.aseprite.Aseprite.yaml new file mode 100644 index 0000000..b1adf33 --- /dev/null +++ b/com.aseprite.Aseprite.yaml @@ -0,0 +1,196 @@ +# Flatpak manifest for Aseprite +# Build using: flatpak-builder --force-clean build-dir com.aseprite.Aseprite.yaml --repo=repo +# Bundle using: flatpak build-bundle repo aseprite.flatpak com.aseprite.Aseprite + +app-id: com.aseprite.Aseprite +runtime: org.freedesktop.Platform +runtime-version: '23.08' +sdk: org.freedesktop.Sdk +command: aseprite +finish-args: + # Graphics and display + - --share=ipc + - --socket=x11 + - --socket=wayland + - --device=dri + # Filesystem access (adjust as needed, 'home' is broad) + - --filesystem=home + # Needed for Skia/fontconfig + - --filesystem=xdg-config/fontconfig:ro + # Allow talking to portals (for file dialogs etc.) + - --talk-name=org.freedesktop.portal.Desktop + - --talk-name=org.freedesktop.portal.Documents + - --talk-name=org.freedesktop.portal.FileChooser +# Add separate state directory to force consistent cache behavior +build-options: + env: + FLATPAK_BUILDER_BUILDDIR: "/tmp/flatpak-builder-dir" +cleanup: + - /include + - /lib/pkgconfig + - /man + - /share/man + - /share/pkgconfig + - '*-debuginfo*' + - '*.la' + +modules: + # 1. Depot Tools (Needed for Skia build) + - name: depot_tools + buildsystem: simple + build-commands: + # depot_tools doesn't need building, just needs to be present. + # Install it into /app/depot_tools within the build environment. + # The source '.' is now /sources/depot_tools + - install -d /app/depot_tools + - cp -r ./* /app/depot_tools/ + sources: + # Use the directory copied into the Docker image + - type: dir + path: /sources/depot_tools + + # 2. Skia (Aseprite Dependency) + - name: skia + buildsystem: simple + build-options: + # Ensure depot_tools is accessible during build + prepend-path: /app/depot_tools + env: + # Needed to locate some of the dependencies we added + PKG_CONFIG_PATH: /app/lib/pkgconfig:/usr/lib/pkgconfig + build-commands: + # Prepare GN arguments + # Using system libs provided by the Freedesktop SDK and our additional modules + - | + GN_ARGS="is_debug=false \ + is_official_build=true \ + skia_use_system_expat=true \ + skia_use_system_icu=true \ + skia_use_system_libjpeg_turbo=false \ + skia_use_system_libpng=true \ + skia_use_system_libwebp=false \ + skia_use_system_zlib=true \ + skia_use_sfntly=false \ + skia_use_freetype=true \ + skia_use_harfbuzz=true \ + skia_pdf_subset_harfbuzz=true \ + skia_use_system_freetype2=false \ + skia_use_system_harfbuzz=false \ + extra_cflags=[\"-mavx\",\"-O3\"] \ + extra_cxxflags=[\"-mavx\",\"-O3\"]" + # Generate build files using gn from depot_tools + - /app/depot_tools/gn gen out/Release-x64 --args="${GN_ARGS}" + # Compile Skia + - ninja -C out/Release-x64 skia modules + # Install Skia library and headers into the /app prefix for the next module + - install -Dm644 out/Release-x64/libskia.a /app/lib/libskia.a + - install -d /app/include/skia + # Copy headers from the source directory '.' which is /sources/skia + - cp -r include/* /app/include/skia/ + # Also copy Skia third_party includes needed by Aseprite + - mkdir -p /app/third_party + - cp -r third_party/externals /app/third_party/ + + # Make symlinks to libraries that Aseprite expects + - mkdir -p /app/lib/pkgconfig + - | + echo "Creating symlinks to libraries embedded in Skia's build..." + # WebP + if [ -f out/Release-x64/libwebp.a ]; then + cp out/Release-x64/libwebp.a /app/lib/ + ln -sf /app/lib/libwebp.a /app/lib/libwebp.so + fi + # JPEG + if [ -f out/Release-x64/libjpeg.a ]; then + cp out/Release-x64/libjpeg.a /app/lib/ + ln -sf /app/lib/libjpeg.a /app/lib/libjpeg-turbo.so + fi + # Freetype + if [ -f out/Release-x64/libfreetype.a ]; then + cp out/Release-x64/libfreetype.a /app/lib/ + ln -sf /app/lib/libfreetype.a /app/lib/libfreetype.so + fi + # Harfbuzz + if [ -f out/Release-x64/libharfbuzz.a ]; then + cp out/Release-x64/libharfbuzz.a /app/lib/ + ln -sf /app/lib/libharfbuzz.a /app/lib/libharfbuzz.so + fi + + # Make sure ICU data file exists and is in the right place + - mkdir -p /app/third_party/externals/icu/flutter + - | + echo "Looking for and copying icudtl.dat to the expected location..." + # Check various possible locations + if [ -f out/Release-x64/icudtl.dat ]; then + cp out/Release-x64/icudtl.dat /app/third_party/externals/icu/flutter/ + echo "Found icudtl.dat in out/Release-x64/" + elif [ -f third_party/externals/icu/common/icudtl.dat ]; then + cp third_party/externals/icu/common/icudtl.dat /app/third_party/externals/icu/flutter/ + echo "Found icudtl.dat in third_party/externals/icu/common/" + elif [ -f third_party/externals/icu/flutter/icudtl.dat ]; then + cp third_party/externals/icu/flutter/icudtl.dat /app/third_party/externals/icu/flutter/ + echo "Found icudtl.dat in third_party/externals/icu/flutter/" + else + echo "Attempting to find icudtl.dat with find command..." + find . -name "icudtl.dat" -exec cp {} /app/third_party/externals/icu/flutter/ \; -print + fi + + # Create an empty file as a fallback if nothing is found + if [ ! -f /app/third_party/externals/icu/flutter/icudtl.dat ]; then + echo "WARNING: icudtl.dat not found. Creating empty placeholder..." + touch /app/third_party/externals/icu/flutter/icudtl.dat + fi + sources: + # Use the directory copied into the Docker image + - type: dir + path: /sources/skia + + # 3. Aseprite + - name: aseprite + # Use cmake-ninja buildsystem for convenience + buildsystem: cmake-ninja + # Set build directory outside of source + builddir: true + # CMake configuration options + config-opts: + - -DCMAKE_BUILD_TYPE=RelWithDebInfo + # Add optimization flags as separate options + - -DCMAKE_CXX_FLAGS=-mavx + - -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=-O3 + # Use Skia backend + - -DLAF_BACKEND=skia + # Point to the Skia installed in the /app prefix by the previous module + - -DSKIA_DIR=/app + - -DSKIA_LIBRARY_DIR=/app/lib + - -DSKIA_LIBRARY=/app/lib/libskia.a + # Use Skia's bundled libraries instead of system libraries + - -DUSE_SHARED_LIBJPEG=OFF + - -DUSE_SHARED_LIBPNG=ON + - -DUSE_SHARED_ZLIB=ON + - -DUSE_SHARED_GIFLIB=ON + - -DUSE_SHARED_WEBP=OFF + - -DUSE_SHARED_FREETYPE=OFF + - -DUSE_SHARED_HARFBUZZ=OFF + # Create various directories that Aseprite might search in + - -DWEBP_INCLUDE_DIR=/app/third_party/externals/libwebp/src + - -DWEBP_LIBRARIES=/app/lib/libwebp.a + - -DLIBJPEG_TURBO_INCLUDE_DIR=/app/third_party/externals/libjpeg-turbo + - -DLIBJPEG_TURBO_LIBRARY=/app/lib/libjpeg.a + - -DFREETYPE_INCLUDE_DIR=/app/third_party/externals/freetype/include + - -DFREETYPE_LIBRARY=/app/lib/libfreetype.a + - -DHARFBUZZ_INCLUDE_DIR=/app/third_party/externals/harfbuzz/src + - -DHARFBUZZ_LIBRARY=/app/lib/libharfbuzz.a + sources: + # Use the directory copied into the Docker image + - type: dir + path: /sources/aseprite + # Note: Submodules are expected to be present due to prepare_sources.sh + # Install desktop file and icon after build/install + post-install: + # Install files from the source directory (/sources/aseprite) + - install -Dm644 /sources/aseprite/data/linux/aseprite.desktop /app/share/applications/com.aseprite.Aseprite.desktop + # Install icon (using the 256px version) + - install -Dm644 /sources/aseprite/data/icons/ase256.png /app/share/icons/hicolor/256x256/apps/com.aseprite.Aseprite.png + # Update caches (optional but recommended) + - update-desktop-database -q /app/share/applications + - gtk-update-icon-cache -f -t /app/share/icons/hicolor || true diff --git a/diff.txt b/diff.txt deleted file mode 100644 index 8b3f01f..0000000 --- a/diff.txt +++ /dev/null @@ -1,448 +0,0 @@ -diff --git a/.gitignore b/.gitignore -index 0b34b0f..8e4a455 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -1,3 +1,3 @@ - .idea --dependencies --output -\ No newline at end of file -+src/ -+target/ -\ No newline at end of file -diff --git a/Dockerfile b/Dockerfile -deleted file mode 100644 -index 261de20..0000000 ---- a/Dockerfile -+++ /dev/null -@@ -1,21 +0,0 @@ --FROM python -- --#Required for tzdata --ENV TZ=Europe/Amsterdam --RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -- --# Install dependencies --RUN apt-get update --RUN apt-get upgrade -y --RUN apt-get install -y git unzip curl build-essential cmake ninja-build libx11-dev libxcursor-dev libxi-dev libgl1-mesa-dev libfontconfig1-dev -- --COPY compile.sh / -- --VOLUME /dependencies --VOLUME /output -- --WORKDIR /output -- --RUN ["chmod", "+x", "/compile.sh"] -- --ENTRYPOINT ["/compile.sh"] -diff --git a/Makefile b/Makefile -index dab6b4c..0984390 100644 ---- a/Makefile -+++ b/Makefile -@@ -1,14 +1,94 @@ --IMAGE_NAME := docker-aseprite -+# --- OS Detection --- -+# Attempt to detect host OS family. Fallback to 'arch'. -+# This logic checks /etc/os-release for ID or ID_LIKE fields. -+# Use $(strip ...) to remove potential leading/trailing whitespace from shell output. -+DETECTED_DISTRO := $(strip $(shell \ -+ if [ -f /etc/os-release ]; then \ -+ . /etc/os-release; \ -+ if echo "$$ID$$ID_LIKE" | grep -q -e arch; then \ -+ echo arch; \ -+ elif echo "$$ID$$ID_LIKE" | grep -q -e debian -e ubuntu; then \ -+ echo debian; \ -+ elif echo "$$ID$$ID_LIKE" | grep -q -e fedora; then \ -+ echo fedora; \ -+ else \ -+ echo arch; \ -+ fi; \ -+ else \ -+ echo arch; \ -+ fi \ -+)) - --build: build-image -- docker run --rm \ -- -v ${PWD}/output:/output \ -- -v ${PWD}/dependencies:/dependencies \ -- ${IMAGE_NAME} -+# Set default TARGET_DISTRO based on detection, allowing user override -+TARGET_DISTRO ?= $(DETECTED_DISTRO) - --build-compose: -- docker-compose build -- docker-compose up -+# --- Configuration --- -+IMAGE_NAME := docker-aseprite-${TARGET_DISTRO} -+DOCKERFILE := Dockerfile.${TARGET_DISTRO} -+OUTPUT_DIR := ${PWD}/output -+TARGET_DIR_IN_IMAGE := /target/aseprite/build/bin - -+# --- Main Targets --- -+# Default target -+all: build -+ -+# Prepare source files by running the script -+# Use .PHONY to ensure it always runs if called directly -+.PHONY: prepare-sources -+prepare-sources: -+ @echo "--- Preparing sources ---" -+ @./prepare_sources.sh -+ -+# Build the Docker image for the target distribution -+# Depends on sources being ready (though prepare-sources isn't a file dependency) - build-image: -- docker build -t ${IMAGE_NAME} . -\ No newline at end of file -+# --- DEBUGGING --- -+ @echo "DEBUG: TARGET_DISTRO='${TARGET_DISTRO}'" -+ @echo "DEBUG: DOCKERFILE='${DOCKERFILE}'" -+# --- END DEBUGGING --- -+ifeq (,$(wildcard ${DOCKERFILE})) -+ $(error Dockerfile for target distribution '${TARGET_DISTRO}' (${DOCKERFILE}) not found) -+endif -+ @echo "--- Building Docker image (${IMAGE_NAME}) using ${DOCKERFILE} ---" -+ @docker build -t ${IMAGE_NAME} -f ${DOCKERFILE} . -+ -+# Extract the compiled binary from the image -+# Depends on the image being built -+.PHONY: extract-binary -+extract-binary: build-image -+ @echo "--- Extracting Aseprite binary from ${IMAGE_NAME} ---" -+ @mkdir -p ${OUTPUT_DIR}/bin -+ @CONTAINER_ID=$$(docker create ${IMAGE_NAME}) && \ -+ docker cp "$${CONTAINER_ID}:${TARGET_DIR_IN_IMAGE}/." "${OUTPUT_DIR}/bin/" && \ -+ docker rm "$${CONTAINER_ID}" > /dev/null -+ @echo "Aseprite binary extracted to ${OUTPUT_DIR}/bin" -+ -+# Main build target: Prepare sources, build image, extract binary for selected distro -+.PHONY: build -+build: prepare-sources build-image extract-binary -+ @echo "--- Build complete for ${TARGET_DISTRO} ---" -+ @echo "Aseprite binary is in ${OUTPUT_DIR}/bin" -+ -+# --- Specific Distribution Targets --- -+.PHONY: build-arch build-debian build-fedora -+ -+# Build for Arch Linux (default if not detected otherwise) -+build-arch: -+ $(MAKE) build TARGET_DISTRO=arch -+ -+# Build for Debian/Ubuntu based systems -+build-debian: -+ $(MAKE) build TARGET_DISTRO=debian -+ -+# Build for Fedora based systems -+build-fedora: -+ $(MAKE) build TARGET_DISTRO=fedora -+ -+# --- Utility Targets --- -+# Clean up downloaded sources and output directory -+.PHONY: clean -+clean: -+ @echo "--- Cleaning up ---" -+ @rm -rf ./src -+ @rm -rf ${OUTPUT_DIR} -+ @echo "Cleanup complete." -diff --git a/README.md b/README.md -index 80f66b9..a0b00f8 100644 ---- a/README.md -+++ b/README.md -@@ -1,53 +1,182 @@ --# Docker Aseprite container -+# Docker Aseprite Builder - --This repository allows you to compile Aseprite without installing any build tools. All that is required is Docker. -+This repository provides a Docker-based environment to compile Aseprite easily. It aims to improve compatibility with your host Linux system by building Aseprite within a Docker container that matches your distribution family (Arch, Debian/Ubuntu, or Fedora). - --After spending hours trying to get Aseprite to compile, I decided to just make a Docker image for it -+## Requirements - --Currently the script checks out Skia version `m102` and Aseprite version `1.2.40`. You can easily change this in `compile.sh` by changing the `-b` flag to the desired versions. -+* Docker -+* Make -+* Git -+* Curl -+* Access to `/etc/os-release` on the host system (for the optional auto-detection feature). -+* **CPU with AVX support:** The compiled binary requires a CPU supporting the AVX instruction set (generally CPUs from 2011/Sandy Bridge/Bulldozer onwards). - --If any of the folders of the projects folder isn't empty, the script will skip checking out the latest versions. In order to re-download, delete the according folder. --* ./dependencies/depot_tools --* ./dependencies/skia --* ./output/aseprite -+## Quick Start - --## Usage -- * Install docker -- * Clone this repository -- * cd into cloned repository -- * Run `make build` or `make build-compose` (The latter will use docker-compose to build the image) -- * Grab a cup of coffee, since this can take quite a while (Compiling build deps, skia, and aseprite) -+1. **Clone:** - --You can now find the compiled version of Aseprite in the `output/aseprite/build/bin` folder -+ ```bash -+ git clone -+ cd docker-aseprite-linux -+ ``` - --## FAQ --If you get the following error when running Aseprite: `./aseprite: error while loading shared libraries: libdeflate.so.0: cannot open shared object file: No such file or directory`, make sure you have libdeflate installed on your system. Please run --`sudo apt install -y libdeflate0 libdeflate-dev` -+2. **Build:** - --If you get the following error: `./aseprite: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No such file or directory`, you'll want to install the OpenSSL 1.1 package/library. You may have only OpenSSL 3.x installed, meanwhile Aseprite still uses the v1.1 library. --* On Arch / Arch based distros, run `sudo pacman -Syu openssl-1.1` --* On Ubuntu try: `sudo apt install -y libssl1.1` -+ ```bash -+ make build -+ ``` - --## License -+3. **Run:** Find the compiled binary in `./output/bin/aseprite`. -+ -+> [!WARNING] -+> **Compatibility Notice:** The compiled binary is dynamically linked. While this builder tries to match your OS family, minor differences between the build environment and your host system's libraries *can* still cause runtime errors. Please see the [Troubleshooting & Compatibility](#troubleshooting--compatibility) section for details. -+ -+## Build Process Explained -+ -+### OS Family Auto-Detection -+ -+When you run `make build`, the `Makefile` attempts to detect your host operating system family by reading `/etc/os-release`. It looks for identifiers to classify your system as: -+ -+* **Arch-based:** (`ID` or `ID_LIKE` contains `arch`) -> Uses `Dockerfile.arch` (See file for base image version) -+* **Debian/Ubuntu-based:** (`ID` or `ID_LIKE` contains `debian` or `ubuntu`) -> Uses `Dockerfile.debian` (See file for base image version) -+* **Fedora-based:** (`ID` or `ID_LIKE` contains `fedora`) -> Uses `Dockerfile.fedora` (See file for base image version) -+ -+**Default:** If detection fails or your OS family isn't recognized, it defaults to using the **Arch Linux** build environment. -+ -+### Build Steps -+ -+The `make build` command performs the following steps automatically: -+ -+1. **Prepare Sources (via `./prepare_sources.sh`):** -+ * Clones `depot_tools`, Skia, and Aseprite source code into the `./src` directory if they don't exist. See the script for specific versions. -+ * Runs Skia's `git-sync-deps` command locally to download Skia's internal build dependencies (like the `gn` tool). -+ * **Note on Skia Sync:** The `git-sync-deps` step can sometimes fail due to network issues or rate limiting (HTTP 429 errors) from Google's servers. The script automatically retries this step up to 3 times (1 initial attempt + 2 retries) with a 10-second delay between attempts. You can control the number of *retries* by setting the `PREPARE_RETRIES` environment variable when running `make` (e.g., `PREPARE_RETRIES=5 make build`). -+2. **Build Docker Image:** Builds a Docker image (e.g., `docker-aseprite-arch`) using the selected `Dockerfile.`. Inside the container: -+ * Necessary build tools and development libraries are installed using the distribution's package manager (`pacman`, `apt`, `dnf`). -+ * `depot_tools` is initialized. -+ * Skia is compiled, configured to use the system-provided libraries (avoids network issues and improves compatibility). -+ * Aseprite is compiled, linking against the compiled Skia and other system libraries. -+3. **Extract Binary:** Copies the compiled `aseprite` binary and its `data` directory from the container to `./output/bin` on your host machine. -+ -+### Overriding Auto-Detection -+ -+If the auto-detection chooses the wrong target, or you want to build for a different distribution family, you can override it: -+ -+```bash -+# Force build using Debian environment -+make build TARGET_DISTRO=debian -+ -+# Force build using Fedora environment -+make build TARGET_DISTRO=fedora -+ -+# Force build using Arch environment -+make build TARGET_DISTRO=arch -+``` -+ -+Alternatively, use the specific make targets: -+ -+```bash -+make build-debian -+make build-fedora -+make build-arch -+``` -+ -+## Troubleshooting & Compatibility -+ -+### Detailed Compatibility Explanation -+ -+Aseprite, like many Linux applications, relies on shared libraries (`.so` files) provided by the operating system (e.g., `libpng.so`, `libfreetype.so`, `libX11.so`). This process is called dynamic linking. -+ -+* **Goal:** This builder aims to improve compatibility by compiling Aseprite inside a Docker container using the same OS *family* as your host system. This means Aseprite links against libraries (e.g., Fedora libraries) that are likely similar to those on your host (e.g., your Fedora installation). -+* **The Challenge (Versioning):** Linux distributions and their libraries are constantly updated. The Docker image used for building (e.g., the one specified in `Dockerfile.fedora`) captures a snapshot of libraries at a specific point in time. Your host system might have slightly older or newer *minor versions* of these libraries (e.g., `libpng.so.1.6.40` in the container vs. `libpng.so.1.6.43` on the host). -+* **ABI Stability:** Library developers usually try very hard to maintain Application Binary Interface (ABI) stability across minor versions. This means newer libraries can often run programs compiled against slightly older versions (backward compatibility). -+* **Potential Issues:** -+ * **Build Newer, Run Older (More Risky):** If the build container has a *newer* library version than your older host system, and Aseprite uses a function only present in that newer library, you will likely get runtime errors ("undefined symbol") on the host. -+ * **Build Older, Run Newer (Usually Safer):** If the build container used an older library version than your newer host system, it's *more likely* to work due to backward compatibility efforts. -+ * **Subtle Breaks:** Very rarely, even minor version changes can introduce subtle ABI breaks causing crashes. -+* **Conclusion:** While building for the correct OS family significantly reduces problems, minor version mismatches between the build environment and your host *can* still cause issues. 100% compatibility is hard to guarantee with dynamic linking. - --MIT License -+### Adapting Dockerfiles for Older Systems (Advanced / Use with Caution) - --Copyright (c) 2025 nilsve -+If you are using an older version of a distribution and encounter runtime errors *after* building with the corresponding family target (e.g., `make build TARGET_DISTRO=fedora`), you *could* try modifying the `Dockerfile.`: - --Permission is hereby granted, free of charge, to any person obtaining a copy --of this software and associated documentation files (the "Software"), to deal --in the Software without restriction, including without limitation the rights --to use, copy, modify, merge, publish, distribute, sublicense, and/or sell --copies of the Software, and to permit persons to whom the Software is --furnished to do so, subject to the following conditions: -+1. **Identify the correct Dockerfile** (e.g., `Dockerfile.fedora`). -+2. **Change the `FROM` line:** Update the base image tag to an older version that matches your system (e.g., change the tag in `FROM fedora:...` to an older one like `fedora:39`). Find available tags on Docker Hub. -+3. **Adjust Dependencies:** Older distributions might have different package names or versions. You may need to adjust the `dnf install` (or `apt`, `pacman`) commands in the Dockerfile if the build fails due to missing packages. -+4. **Rebuild:** Run `make build TARGET_DISTRO=fedora` again. - --The above copyright notice and this permission notice shall be included in all --copies or substantial portions of the Software. -+**Disclaimer:** This is an advanced procedure. It might not work, may require significant troubleshooting of package dependencies in the Dockerfile, and is generally less recommended than upgrading your host operating system. -+ -+### Common Runtime Errors -+ -+* **`./aseprite: error while loading shared libraries: libfoo.so.X: cannot open shared object file: No such file or directory`**: Your host system is missing the required library `libfoo.so.X`. Install the package that provides this library using your system's package manager (e.g., `sudo dnf install foo`, `sudo apt install libfooX`, `sudo pacman -S foo`). Common examples needed by Aseprite or its dependencies include `libXcursor`, `libXi`, `freetype`, `harfbuzz`, `libstdc++`. -+* **`./aseprite: ./aseprite: symbol lookup error: ./aseprite: undefined symbol: some_function_name`**: This usually indicates an ABI incompatibility, often because the library version on your host is older than the one used during the build and is missing `some_function_name`. See the compatibility notes above. Upgrading your host system or trying the advanced Dockerfile adaptation might be necessary. -+* **`./aseprite: Illegal instruction`**: This typically means the compiled binary is trying to use CPU instructions not supported by your processor. This build requires a CPU with **AVX** support. -+ -+### Build Failures -+ -+* **Resource Exhaustion:** Compiling Skia and Aseprite requires significant RAM and disk space. Ensure your system (and Docker) has adequate resources allocated. -+* **Network Issues / Rate Limiting:** `prepare_sources.sh` needs to clone repositories and sync Skia's dependencies (`git-sync-deps`), which involves many network requests. -+ * Ensure you have a stable internet connection. Check for proxy issues if applicable. -+ * The Skia sync step might hit rate limits (HTTP 429 errors) from Google's servers. The script automatically retries this step (default: 3 total attempts). If it consistently fails, wait a while before trying again, or increase the number of retries via the `PREPARE_RETRIES` environment variable (e.g., `PREPARE_RETRIES=5 make build`). -+* **Package Not Found (Dockerfile):** If a `dnf`, `apt`, or `pacman` command fails inside the Docker build, a package name might be incorrect for the chosen base image version, or the distribution's repositories might be temporarily unavailable. -+* **Compilation Errors:** Check the detailed build log output from Docker for C++/CMake errors. These could indicate issues with the source code versions, compiler compatibility, or missing dependencies. -+ -+### Auto-Detection Failure -+ -+If the Makefile defaults to Arch but you use a different distribution, it means the detection logic couldn't identify your OS from `/etc/os-release`. Use the `TARGET_DISTRO` variable or specific make targets (`make build-debian`, `make build-fedora`) to select the correct environment. -+ -+## Advanced Usage -+ -+The `make build` command automatically runs `./prepare_sources.sh` without any special flags. If you need to control the source preparation step (e.g., force a re-download or check integrity), you can run the script manually *before* running `make build`, or pass flags through `make`. -+ -+### Running `prepare_sources.sh` Manually -+ -+You can run the script directly with its flags: -+ -+* **Force Re-download Sources:** Deletes `./src` and clones fresh. Useful for corruption or persistent sync issues. -+ ```bash -+ ./prepare_sources.sh --force-redownload -+ make build # Now run the build with fresh sources -+ ``` -+* **Check Source Integrity:** Runs `git fsck` on existing source repos. Does not re-download unless corruption is found and you *then* run with `--force-redownload`. -+ ```bash -+ ./prepare_sources.sh --check-integrity -+ # If no errors, proceed with make build -+ # If errors, consider running with --force-redownload -+ ``` -+* **Set Sync Retries:** Control the number of retries for the Skia `git-sync-deps` step (default is 2 retries, total 3 attempts). -+ ```bash -+ PREPARE_RETRIES=5 ./prepare_sources.sh -+ make build -+ ``` -+ -+### Passing Options via `make` -+ -+While you can run the script manually first, you can also influence the `prepare_sources.sh` step when running `make build` by passing variables: -+ -+* **Set Sync Retries via `make`:** -+ ```bash -+ PREPARE_RETRIES=5 make build -+ ``` -+* **Force Re-download via `make` (More Complex):** The `Makefile` doesn't directly support passing flags like `--force-redownload` to the script. The recommended way is to run the script manually first as shown above, or to run `make clean` followed by `make build`. -+ ```bash -+ make clean && make build # Clean first, then build (includes fresh source prep) -+ ``` -+ -+### Clean Up -+ -+To remove the downloaded sources (`./src`) and the build output (`./output`): -+ -+```bash -+make clean -+``` -+ -+## Notes -+ -+* **Source Versions:** See `prepare_sources.sh` for the specific Git tags of Skia and Aseprite that are cloned. -+* **System Libraries:** Skia is built using system libraries from the container to improve compatibility and avoid build-time network issues. -+ -+## License - --THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, --FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE --AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER --LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, --OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE --SOFTWARE. -+MIT License (Refer to the original license text if needed). -diff --git a/compile.sh b/compile.sh -deleted file mode 100755 -index d53eafb..0000000 ---- a/compile.sh -+++ /dev/null -@@ -1,53 +0,0 @@ --#!/bin/bash -- --# Fail on errors --set -e -- --echo "Download and compile Skia & other dependencies" --cd /dependencies -- --if [ ! -d "/dependencies/depot_tools" ] --then -- git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git --fi -- --if [ ! -d "/dependencies/skia" ] --then -- git clone -b aseprite-m102 https://github.com/aseprite/skia.git --fi -- --export PATH="${PWD}/depot_tools:${PATH}" -- --cd skia --pwd --echo "Syncing skia dependencies" --python3 tools/git-sync-deps -- --echo "Compiling skia" --gn gen out/Release-x64 --args="is_debug=false is_official_build=true skia_use_system_expat=false skia_use_system_icu=false skia_use_system_libjpeg_turbo=false skia_use_system_libpng=false skia_use_system_libwebp=false skia_use_system_zlib=false skia_use_sfntly=false skia_use_freetype=true skia_use_harfbuzz=true skia_pdf_subset_harfbuzz=true skia_use_system_freetype2=false skia_use_system_harfbuzz=false" --ninja -C out/Release-x64 skia modules -- --echo "Download Aseprite and compile" --cd /output -- --if [ ! -d "/output/aseprite" ] --then -- git clone -b v1.2.40 --recursive https://github.com/aseprite/aseprite.git --fi -- --cd aseprite --mkdir -p build --cd build -- --echo "Compiling Asperite" --cmake \ -- -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -- -DLAF_BACKEND=skia \ -- -DSKIA_DIR=/dependencies/skia \ -- -DSKIA_LIBRARY_DIR=/dependencies/skia/out/Release-x64 \ -- -DSKIA_LIBRARY=/dependencies/skia/out/Release-x64/libskia.a \ -- -G Ninja \ -- .. -- --echo "Linking Aseprite" --ninja aseprite -diff --git a/docker-compose.yml b/docker-compose.yml -deleted file mode 100644 -index 18c269a..0000000 ---- a/docker-compose.yml -+++ /dev/null -@@ -1,8 +0,0 @@ --version: '2' -- --services: -- installer: -- build: . -- volumes: -- - "./output:/output:rw" -- - "./dependencies:/dependencies:rw" diff --git a/prepare_sources.sh b/prepare_sources.sh index 45aed4c..0e05540 100755 --- a/prepare_sources.sh +++ b/prepare_sources.sh @@ -37,8 +37,8 @@ print_usage() { } _CURRENT_STEP=0 -_TOTAL_STEPS=${#REPOS[@]} # Number of repositories +1 for Skia sync -_TOTAL_STEPS=$((_TOTAL_STEPS + 1)) +_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)) @@ -265,6 +265,29 @@ for repo_info in "${REPOS[@]}"; do 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+)