forked from aki/docker-aseprite-linux
226 lines
16 KiB
Markdown
226 lines
16 KiB
Markdown
# Docker Aseprite 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).
|
|
|
|
## Requirements
|
|
|
|
* 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).
|
|
|
|
## Quick Start
|
|
|
|
1. **Clone:**
|
|
|
|
```bash
|
|
git clone <repository-url>
|
|
cd docker-aseprite-linux
|
|
```
|
|
|
|
2. **Build:**
|
|
|
|
```bash
|
|
make build
|
|
```
|
|
|
|
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.<distro>`. 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.
|
|
|
|
### 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.<distro>`:
|
|
|
|
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.
|
|
|
|
### 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.
|
|
* **Major Build System Refactoring (Improved Reliability & Path to Portability)**
|
|
|
|
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.
|
|
|
|
**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.**
|
|
|
|
## License
|
|
|
|
[MIT License](LICENSE) |