Dockerfile, AppImage Release

* Add a Dockerfile to this repo for self-contained easy releases.
  Run it from an x86_64 Linux host and it will produce 64-bit and
  32-bit Linux (rpm, deb, AppImage, tar.gz) and Windows releases.
* The `make appimage` command is more self-sufficient: it will
  download the appimagetool-x86_64.AppImage program for your $ARCH
  for an easy no-dependencies run after you have run `make dist`
This commit is contained in:
Noah 2022-12-07 21:34:12 -08:00
parent 56c03dda45
commit cbc8682406
5 changed files with 200 additions and 38 deletions

View File

@ -8,13 +8,33 @@
* [Windows Cross-Compile from Linux](#windows-cross-compile-from-linux) * [Windows Cross-Compile from Linux](#windows-cross-compile-from-linux)
* [Old Docs](#old-docs) * [Old Docs](#old-docs)
# Dockerfile
The Dockerfile in this git repo may be the quickest way to fully
release the game for as many platforms as possible. Run it from a
64-bit host Linux system and it will generate Linux and Windows
releases for 64-bit and 32-bit Intel CPUs.
It depends on your git clone of doodle to be fully initialized
(e.g., you have run the bootstrap.py script and a `make dist`
would build a release for your current system, with doodads and
runtime assets all in the right places).
Run `make docker` and the results will be in the
`artifacts/release` folder in your current working directory.
**Fedora notes (SELinux):** if you run this from a Fedora host
you will need to `sudo setenforce permissive` to allow the
Dockerfile to mount the artifacts/release folder to export its
results.
# Automated Release Scripts # Automated Release Scripts
For the quickest ways to fully end-to-end build Sketchy Maze for various Other Dockerfiles and scripts used to release the game:
platforms to produce public release artifacts, see the following repos:
* [SketchyMaze/docker](https://git.kirsle.net/SketchyMaze/docker) provides a Dockerfile * [SketchyMaze/docker](https://git.kirsle.net/SketchyMaze/docker) provides a Dockerfile
that fully end-to-end releases the latest version of the game for Linux and Windows: that fully end-to-end releases the latest version of the game for Linux and Windows. 64bit and 32bit versions that freshly clone the
game from git and output their respective CPU release artifacts:
* Windows: .zip file * Windows: .zip file
* Linux: .tar.gz, .rpm, .deb * Linux: .tar.gz, .rpm, .deb
* [flatpak](https://code.sketchymaze.com/game/flatpak) is a Flatpak manifest for * [flatpak](https://code.sketchymaze.com/game/flatpak) is a Flatpak manifest for

118
Dockerfile Normal file
View File

@ -0,0 +1,118 @@
##
# Fully build and distribute Linux and Windows binaries for Project: Doodle.
#
# This is designed to be run from a fully initialized Doodle environment
# (you had run the bootstrap.py for your system, and the doodads and
# levelpacks are installed in the assets/ folder, and `make dist` would
# build a release quality game for your local machine).
#
# It will take your working directory (minus any platform-specific artifacts
# and git repos cloned in to your deps/ folder) and build them from a sane
# Debian base and generate full release artifacts for:
#
# - Linux (x86_64 and i686) as .rpm, .deb, .flatpak and .tar.gz
# - Windows (64-bit and 32-bit) as .zip
#
# Artifact outputs will be in the dist/mw/ folder.
##
FROM debian:latest AS build64
ENV GOPATH /go
ENV GOPROXY direct
ENV PATH /opt/go/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/go/bin
# Install all dependencies.
RUN apt update && apt -y install git zip tar libsdl2-dev libsdl2-ttf-dev \
libsdl2-mixer-dev gcc-mingw-w64-x86-64 gcc make wget \
flatpak-builder ruby-dev gcc rpm libffi-dev \
ruby-dev ruby-rubygems rpm libffi-dev rsync
RUN gem install fpm; exit 0
# Download and install modern Go.
WORKDIR /root
RUN wget https://go.dev/dl/go1.19.3.linux-amd64.tar.gz -O go.tgz && \
tar -xzf go.tgz && \
cp -r go /opt/go
# Add some cacheable directories to speed up Dockerfile trial-and-error.
ADD deps/vendor /SketchyMaze/deps/vendor
# MinGW setup for Windows executable cross-compile.
WORKDIR /SketchyMaze/deps/vendor/mingw-libs
RUN for i in *.tar.gz; do tar -xzvf $i; done
RUN cp -r SDL2-2.0.9/x86_64-w64-mingw32 /usr && \
cp -r SDL2_mixer-2.0.4/x86_64-w64-mingw32 /usr && \
cp -r SDL2_ttf-2.0.15/x86_64-w64-mingw32 /usr
RUN mkdir -p /usr/lib/golang/pkg/windows_amd64
WORKDIR /SketchyMaze
RUN mkdir -p bin && cp deps/vendor/DLL/*.dll bin/
# Add the current working directory (breaks the docker cache every time).
ADD . /SketchyMaze
# Fetch the guidebook.
# RUN sh -c '[[ ! -d ./guidebook ]] && wget -O - https://download.sketchymaze.com/guidebook.tar.gz | tar -xzvf -'
# Use go-winres on the Windows exe (embed application icons)
RUN go install github.com/tc-hib/go-winres@latest && go-winres make
# Install Go dependencies and do the thing:
# - builds the program for Linux
# - builds for Windows via MinGW
# - runs `make dist/` creating an uber build for both OS's
# - runs release.sh to carve out the Linux and Windows versions and
# zip them all up nicely.
RUN make setup && make from-docker64
# Collect the build artifacts.
RUN mkdir -p artifacts && cp -rv dist/release ./artifacts/
###
# 32-bit Dockerfile version of the above
###
FROM i386/debian:latest AS build32
ENV GOPATH /go
ENV GOPROXY direct
ENV PATH /opt/go/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/go/bin
# Dependencies, note the w64-i686 difference to the above
RUN apt update && apt -y install git zip tar libsdl2-dev libsdl2-ttf-dev \
libsdl2-mixer-dev gcc-mingw-w64-i686 gcc make wget \
flatpak-builder ruby-dev gcc rpm libffi-dev \
ruby-dev ruby-rubygems rpm libffi-dev
RUN gem install fpm; exit 0
# Download and install modern Go.
WORKDIR /root
RUN wget https://go.dev/dl/go1.19.3.linux-386.tar.gz -O go.tgz && \
tar -xzf go.tgz && \
cp -r go /opt/go
COPY --from=build64 /SketchyMaze /SketchyMaze
# MinGW setup for Windows executable cross-compile.
WORKDIR /SketchyMaze/deps/vendor/mingw-libs
RUN for i in *.tar.gz; do tar -xzvf $i; done
RUN cp -r SDL2-2.0.9/i686-w64-mingw32 /usr && \
cp -r SDL2_mixer-2.0.4/i686-w64-mingw32 /usr && \
cp -r SDL2_ttf-2.0.15/i686-w64-mingw32 /usr
RUN mkdir -p /usr/lib/golang/pkg/windows_386
WORKDIR /SketchyMaze
RUN mkdir -p bin && cp deps/vendor/DLL-32bit/*.dll bin/
# Do the thing.
RUN make setup && make from-docker32
# Collect the build artifacts.
RUN mkdir -p artifacts && cp -rv dist/release ./artifacts/
###
# Back to (64bit) base for the final CMD to copy artifacts out.
###
FROM debian:latest
COPY --from=build32 /SketchyMaze /SketchyMaze
CMD ["cp", "-r", "-v", \
"/SketchyMaze/artifacts/release/", \
"/mnt/export/"]

View File

@ -71,7 +71,7 @@ doodads:
# `make mingw` to cross-compile a Windows binary with mingw. # `make mingw` to cross-compile a Windows binary with mingw.
.PHONY: mingw .PHONY: mingw
mingw: doodads mingw:
env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \ env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \ GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
go build $(LDFLAGS_W) -i -o bin/sketchymaze.exe cmd/doodle/main.go go build $(LDFLAGS_W) -i -o bin/sketchymaze.exe cmd/doodle/main.go
@ -81,7 +81,7 @@ mingw: doodads
# `make mingw32` to cross-compile a Windows binary with mingw (32-bit). # `make mingw32` to cross-compile a Windows binary with mingw (32-bit).
.PHONY: mingw32 .PHONY: mingw32
mingw32: doodads mingw32:
env CGO_ENABLED="1" CC="/usr/bin/i686-w64-mingw32-gcc" \ env CGO_ENABLED="1" CC="/usr/bin/i686-w64-mingw32-gcc" \
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \ GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
go build $(LDFLAGS_W) -i -o bin/sketchymaze.exe cmd/doodle/main.go go build $(LDFLAGS_W) -i -o bin/sketchymaze.exe cmd/doodle/main.go
@ -91,7 +91,7 @@ mingw32: doodads
# `make mingw-free` for Windows binary in free mode. # `make mingw-free` for Windows binary in free mode.
.PHONY: mingw-free .PHONY: mingw-free
mingw-free: doodads mingw-free:
env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \ env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \ GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
go build $(LDFLAGS_W) -tags="shareware" -i -o bin/sketchymaze.exe cmd/doodle/main.go go build $(LDFLAGS_W) -tags="shareware" -i -o bin/sketchymaze.exe cmd/doodle/main.go
@ -125,6 +125,17 @@ mingw-release: doodads build mingw __dist-common release
.PHONY: mingw32-release .PHONY: mingw32-release
mingw32-release: doodads build mingw32 __dist-common release32 mingw32-release: doodads build mingw32 __dist-common release32
# `make from-docker64` is an internal command run by the Dockerfile to build the
# game - assumes doodads and assets are in the right spot already.
.PHONY: from-docker64
.PHONY: from-docker32
from-docker64: build mingw __dist-common
ARCH=x86_64 make appimage
make release
from-docker32: build mingw32 __dist-common
ARCH=i686 make appimage
make release32
# `make osx` to cross-compile a Mac OS binary with osxcross. # `make osx` to cross-compile a Mac OS binary with osxcross.
# .PHONY: osx # .PHONY: osx
# osx: doodads # osx: doodads
@ -155,6 +166,14 @@ test:
.PHONY: dist .PHONY: dist
dist: doodads build __dist-common dist: doodads build __dist-common
# `make docker` runs the Dockerfile to do a full release for 64-bit and 32-bit Linux
# and Windows apps.
.PHONY: docker
docker:
mkdir -p docker-artifacts
podman build -t doodle_docker .
podman run --rm --mount type=bind,src=$(shell pwd)/docker-artifacts,dst=/mnt/export doodle_docker
# `make dist-free` builds and tars up a release in shareware mode. # `make dist-free` builds and tars up a release in shareware mode.
.PHONY: dist-free .PHONY: dist-free
dist-free: doodads build-free __dist-common dist-free: doodads build-free __dist-common
@ -171,21 +190,6 @@ __dist-common:
cd dist && tar -czvf sketchymaze-$(VERSION).tar.gz sketchymaze-$(VERSION) cd dist && tar -czvf sketchymaze-$(VERSION).tar.gz sketchymaze-$(VERSION)
cd dist && zip -r sketchymaze-$(VERSION).zip sketchymaze-$(VERSION) cd dist && zip -r sketchymaze-$(VERSION).zip sketchymaze-$(VERSION)
# `make docker` to run the Docker builds
.PHONY: docker docker.ubuntu docker.debian docker.fedora __docker.dist
docker.ubuntu:
mkdir -p docker/ubuntu
./docker/dist-ubuntu.sh
docker.debian:
mkdir -p docker/debian
./docker/dist-debian.sh
docker.fedora:
mkdir -p docker/fedora
./docker/dist-fedora.sh
docker: docker.ubuntu docker.debian docker.fedora
__docker.dist: dist
cp dist/*.tar.gz dist/*.zip /mnt/export/
# `make clean` cleans everything up. # `make clean` cleans everything up.
.PHONY: clean .PHONY: clean
clean: clean:

View File

@ -1,20 +1,19 @@
#!/bin/bash #!/bin/bash
# Script to build an AppImage. # Script to build an AppImage.
# # Run it like `ARCH=x86_64 make appimage`
# Dependencies: # It will fetch your appimagetool-x86_64.AppImage program to build the appimage.
# * appimage-builder: a Python module, so pip install -r requirements.txt
if ! command -v appimage-builder &> /dev/null;then
echo "appimage-builder not found; run pip install -r requirements.txt"
exit 1
fi
if [[ ! -d "./dist/sketchymaze-latest" ]]; then if [[ ! -d "./dist/sketchymaze-latest" ]]; then
echo "error: run make dist before make appimage" echo "error: run make dist before make appimage"
exit 1 exit 1
fi fi
if [[ "$ARCH" == "" ]]; then
echo "You should set ARCH=x86_64 (or your platform for AppImage output)"
exit 1
fi
APPDIR="./dist/AppDir" APPDIR="./dist/AppDir"
LAUNCHER="./scripts/appimage-launcher.sh" LAUNCHER="./scripts/appimage-launcher.sh"
DESKTOP="./scripts/appimage.desktop" DESKTOP="./scripts/appimage.desktop"
@ -24,6 +23,13 @@ ICON_VECTOR="./etc/icons/orange-128.svg"
APP_RUN="$APPDIR/AppRun" APP_RUN="$APPDIR/AppRun"
DIR_ICON="$APPDIR/sketchymaze.svg" DIR_ICON="$APPDIR/sketchymaze.svg"
APPIMAGETOOL="appimagetool-$ARCH.AppImage"
if [[ ! -f "./$APPIMAGETOOL" ]]; then
echo "Downloading appimagetool"
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$ARCH.AppImage"
chmod a+x $APPIMAGETOOL
fi
# Clean start # Clean start
if [[ -d "$APPDIR" ]]; then if [[ -d "$APPDIR" ]]; then
echo "Note: Removing previous $APPDIR" echo "Note: Removing previous $APPDIR"
@ -49,4 +55,4 @@ rsync -av "./dist/sketchymaze-latest/" "$APPDIR/"
echo "Making AppImage..." echo "Making AppImage..."
cd $APPDIR cd $APPDIR
appimagetool $(pwd) ../../$APPIMAGETOOL $(pwd)

View File

@ -14,7 +14,7 @@ the repos easily. This script will also handle installing the SDL2 dependencies
on Fedora, Debian and macOS type systems. on Fedora, Debian and macOS type systems.
""" """
import sys import argparse
import os import os
import os.path import os.path
import subprocess import subprocess
@ -49,7 +49,7 @@ dep_arch = ["go", "sdl2", "sdl2_ttf", "sdl2_mixer"]
ROOT = pathlib.Path().absolute() ROOT = pathlib.Path().absolute()
def main(): def main(fast=False):
print( print(
"Project: Doodle Full Installer\n\n" "Project: Doodle Full Installer\n\n"
"Current working directory: {root}\n\n" "Current working directory: {root}\n\n"
@ -60,7 +60,7 @@ def main():
.format(root=ROOT) .format(root=ROOT)
) )
input("Press Enter to begin.") input("Press Enter to begin.")
install_deps() install_deps(fast)
clone_repos() clone_repos()
patch_gomod() patch_gomod()
copy_assets() copy_assets()
@ -68,22 +68,25 @@ def main():
build() build()
def install_deps(): def install_deps(fast):
"""Install system dependencies.""" """Install system dependencies."""
if fast:
fast = " -y"
if shell("which rpm") == 0 and shell("which dnf") == 0: if shell("which rpm") == 0 and shell("which dnf") == 0:
# Fedora-like. # Fedora-like.
if shell("rpm -q {}".format(' '.join(dep_fedora))) != 0: if shell("rpm -q {}".format(' '.join(dep_fedora))) != 0:
must_shell("sudo dnf install {}".format(' '.join(dep_fedora))) must_shell("sudo dnf install {}{}".format(' '.join(dep_fedora)), fast)
elif shell("which brew") == 0: elif shell("which brew") == 0:
# MacOS, as Catalina has an apt command now?? # MacOS, as Catalina has an apt command now??
must_shell("brew install {}".format(' '.join(dep_macos))) must_shell("brew install {} {}".format(' '.join(dep_macos)), fast)
elif shell("which apt") == 0: elif shell("which apt") == 0:
# Debian-like. # Debian-like.
if shell("dpkg-query -l {}".format(' '.join(dep_debian))) != 0: if shell("dpkg-query -l {}".format(' '.join(dep_debian))) != 0:
must_shell("sudo apt update && sudo apt install {}".format(' '.join(dep_debian))) must_shell("sudo apt update && sudo apt install {}{}".format(' '.join(dep_debian)), fast)
elif shell("which pacman") == 0: elif shell("which pacman") == 0:
# Arch-like. # Arch-like.
must_shell("sudo pacman -S {}".format(' '.join(dep_arch))) must_shell("sudo pacman -S{} {}{}".format(fast, ' '.join(dep_arch)))
else: else:
print("Warning: didn't detect your package manager to install SDL2 and other dependencies") print("Warning: didn't detect your package manager to install SDL2 and other dependencies")
@ -148,6 +151,17 @@ def must_shell(cmd):
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser("doodle bootstrap")
parser.add_argument("fast", "f",
action="store_true",
help="Run the script non-interactively (yes to your package manager, git clone over https)",
)
args = parser.parse_args()
if args.fast:
main(fast=args.fast)
quit()
if not input("Use ssh to git clone these repos? [yN] ").lower().startswith("y"): if not input("Use ssh to git clone these repos? [yN] ").lower().startswith("y"):
keys = list(repos.keys()) keys = list(repos.keys())
for k in keys: for k in keys: