From cbc8682406e74490ecad7bdf11db388293c60a01 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Wed, 7 Dec 2022 21:34:12 -0800 Subject: [PATCH] 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` --- Building.md | 26 ++++++++++-- Dockerfile | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ Makefile | 40 +++++++++-------- appimage.sh | 24 +++++++---- bootstrap.py | 30 +++++++++---- 5 files changed, 200 insertions(+), 38 deletions(-) create mode 100644 Dockerfile diff --git a/Building.md b/Building.md index 35ca127..ece1ac9 100644 --- a/Building.md +++ b/Building.md @@ -8,13 +8,33 @@ * [Windows Cross-Compile from Linux](#windows-cross-compile-from-linux) * [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 -For the quickest ways to fully end-to-end build Sketchy Maze for various -platforms to produce public release artifacts, see the following repos: +Other Dockerfiles and scripts used to release the game: * [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 * Linux: .tar.gz, .rpm, .deb * [flatpak](https://code.sketchymaze.com/game/flatpak) is a Flatpak manifest for diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..842c18b --- /dev/null +++ b/Dockerfile @@ -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/"] \ No newline at end of file diff --git a/Makefile b/Makefile index 78a3c93..54629da 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ doodads: # `make mingw` to cross-compile a Windows binary with mingw. .PHONY: mingw -mingw: doodads +mingw: env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \ GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \ 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). .PHONY: mingw32 -mingw32: doodads +mingw32: env CGO_ENABLED="1" CC="/usr/bin/i686-w64-mingw32-gcc" \ GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \ 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. .PHONY: mingw-free -mingw-free: doodads +mingw-free: env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \ 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 @@ -125,6 +125,17 @@ mingw-release: doodads build mingw __dist-common release .PHONY: mingw32-release 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. # .PHONY: osx # osx: doodads @@ -155,6 +166,14 @@ test: .PHONY: dist 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. .PHONY: dist-free 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 && 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. .PHONY: clean clean: diff --git a/appimage.sh b/appimage.sh index cadd273..9aac469 100755 --- a/appimage.sh +++ b/appimage.sh @@ -1,20 +1,19 @@ #!/bin/bash # Script to build an AppImage. -# -# Dependencies: -# * 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 +# Run it like `ARCH=x86_64 make appimage` +# It will fetch your appimagetool-x86_64.AppImage program to build the appimage. if [[ ! -d "./dist/sketchymaze-latest" ]]; then echo "error: run make dist before make appimage" exit 1 fi +if [[ "$ARCH" == "" ]]; then + echo "You should set ARCH=x86_64 (or your platform for AppImage output)" + exit 1 +fi + APPDIR="./dist/AppDir" LAUNCHER="./scripts/appimage-launcher.sh" DESKTOP="./scripts/appimage.desktop" @@ -24,6 +23,13 @@ ICON_VECTOR="./etc/icons/orange-128.svg" APP_RUN="$APPDIR/AppRun" 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 if [[ -d "$APPDIR" ]]; then echo "Note: Removing previous $APPDIR" @@ -49,4 +55,4 @@ rsync -av "./dist/sketchymaze-latest/" "$APPDIR/" echo "Making AppImage..." cd $APPDIR -appimagetool $(pwd) \ No newline at end of file +../../$APPIMAGETOOL $(pwd) \ No newline at end of file diff --git a/bootstrap.py b/bootstrap.py index 87bccff..ac7f7fd 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -14,7 +14,7 @@ the repos easily. This script will also handle installing the SDL2 dependencies on Fedora, Debian and macOS type systems. """ -import sys +import argparse import os import os.path import subprocess @@ -49,7 +49,7 @@ dep_arch = ["go", "sdl2", "sdl2_ttf", "sdl2_mixer"] ROOT = pathlib.Path().absolute() -def main(): +def main(fast=False): print( "Project: Doodle Full Installer\n\n" "Current working directory: {root}\n\n" @@ -60,7 +60,7 @@ def main(): .format(root=ROOT) ) input("Press Enter to begin.") - install_deps() + install_deps(fast) clone_repos() patch_gomod() copy_assets() @@ -68,22 +68,25 @@ def main(): build() -def install_deps(): +def install_deps(fast): """Install system dependencies.""" + if fast: + fast = " -y" + if shell("which rpm") == 0 and shell("which dnf") == 0: # Fedora-like. 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: # 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: # Debian-like. 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: # Arch-like. - must_shell("sudo pacman -S {}".format(' '.join(dep_arch))) + must_shell("sudo pacman -S{} {}{}".format(fast, ' '.join(dep_arch))) else: 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__": + 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"): keys = list(repos.keys()) for k in keys: