From 56ba80c93555b0bfc49a49026a1707103fbbed77 Mon Sep 17 00:00:00 2001 From: Sean Hickey Date: Fri, 30 Sep 2022 01:30:25 -0700 Subject: [PATCH] Go down a rabbit hole of trying to statically compile on linux --- Dockerfile | 14 ++++++++ Makefile | 38 ++++++++++++-------- docs/builds/linux-static-release.md | 51 +++++++++++++++++++++++++++ docs/builds/windows-static-release.md | 16 +++++++++ scripts/build-windows-386.sh | 5 +++ scripts/build-windows-am64.sh | 5 +++ scripts/build-windows-static.sh | 19 ---------- scripts/common-release-build.sh | 38 ++++++++++++++++++++ 8 files changed, 153 insertions(+), 33 deletions(-) create mode 100644 Dockerfile create mode 100644 docs/builds/linux-static-release.md create mode 100644 docs/builds/windows-static-release.md create mode 100644 scripts/build-windows-386.sh create mode 100644 scripts/build-windows-am64.sh delete mode 100644 scripts/build-windows-static.sh create mode 100644 scripts/common-release-build.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..57cb3c8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM alpine:latest + +RUN apk add --no-cache \ + gcc musl-dev make go \ + alsa-lib libxrandr \ + sdl2-dev sdl2_mixer-dev sdl2_image-dev sdl2_ttf-dev + +WORKDIR /home + +COPY . . + +#RUN make release_static + +#COPY ./project-ely ./project-ely-static diff --git a/Makefile b/Makefile index 39d3296..58eec93 100644 --- a/Makefile +++ b/Makefile @@ -1,42 +1,52 @@ EXECUTABLE=project-ely + GO=go +MINGW64=x86_64-w64-mingw32-gcc +MINGW32=i686-w64-mingw32-gcc +MUSL_GCC="${HOME}/musl/bin/musl-gcc -static" + GO_BUILD=build -x -v GO_BUILD_DEBUG=${GO_BUILD} -gcflags=all="-N -l" -GO_BUILD_RELEASE=${GO_BUILD} -trimpath -tags static -ldflags "-s -w" -GO_BUILD_RELEASE_WINDOWS=${GO_BUILD} -trimpath -tags static -ldflags "-s -w -H windowsgui" +GO_BUILD_RELEASE=${GO_BUILD} -trimpath -ldflags '-s -w' +GO_BUILD_RELEASE_STATIC=${GO_BUILD} -trimpath -ldflags '-s -w -linkmode external -extldflags "-static"' all: release dependencies: ${GO} mod tidy -debug: linter dependencies +prebuild: dependencies + +debug: prebuild ${GO} ${GO_BUILD_DEBUG} -release: linter dependencies +release: prebuild ${GO} ${GO_BUILD_RELEASE} +# release_static only works on Alpine, see the Dockerfile +release_static: prebuild + CGO_ENABLED=1 \ + CGO_LDFLAGS="-Wl,-rpath -L${HOME}/pkg/lib -L/usr/lib -L/usr/lib/x86_64-linux-gnu" \ + ${GO} ${GO_BUILD_RELEASE_STATIC} + linter: golangci-lint run clean: rm -f ${EXECUTABLE} + rm -f ${EXECUTABLE}-* rm -f output.log rm -f cpu.prof cross_windows: - CGO_ENABLED=1 \ - CC=x86_64-w64-mingw32-gcc \ - GOOS=windows \ - GOARCH=amd64 \ - ${GO} ${GO_BUILD_RELEASE} + CC=${MINGW64} \ + NAME=${EXECUTABLE} \ + ./scripts/build-windows-amd64.sh cross_windows_x86: - CGO_ENABLED=1 \ - CC=i686-w64-mingw32-gcc \ - GOOS=windows \ - GOARCH=386 \ - ${GO} ${GO_BUILD_RELEASE} + CC=${MINGW32} \ + NAME=${EXECUTABLE} \ + ./scripts/build-windows-386.sh goimports_everything: find . -name "*.go" -exec goimports -w {} \; diff --git a/docs/builds/linux-static-release.md b/docs/builds/linux-static-release.md new file mode 100644 index 0000000..6ec99ea --- /dev/null +++ b/docs/builds/linux-static-release.md @@ -0,0 +1,51 @@ +# Linux Static Release Build + +Because we're depending on SDL and X11 libraries, we can't easily +build a completely statically-linked binary using musl unless we +recompile those library dependencies also using musl. I was able to +build something that was mostly statically-linked except for libc, but +then it didn't run in a base Fedora Docker container. + +The workaround for this is to use something where everything is +already compiled with musl, like an Alpine Linux Docker container. + +To build a fully statically-linked binary, use the `Dockerfile` in the +root directory. + +```sh +docker build -t project-ely-build . +``` + +This should spit out an executable called `project-ely-static`. + +## Attempts on Debian + +I tried installing musl on Debian. + +I installed it to my home directory. I also specified my pkgsrc version of GCC. +```sh +CC=$HOME/pkg/gcc10/bin/gcc +./configure --disabled-shared --prefix=$HOME/musl +make -j4 +make install +``` + +Then I had to symlink the X11 include dir. + +```sh +cd musl-install/include +ln -s /usr/include/X11 X11 +``` + +I ran into an issue where it was unable to find many basic libraries +like Xrandr and asound. But then I hit that same issue with the Alpine +container =( + +## References + +Before giving up and using Alpine, I found these cool links that +suggest you could do this more easily if we only depended on the C std +library stuff, no external dependencies like X11 and SDL. + +* https://honnef.co/posts/2015/06/statically_compiled_go_programs__always__even_with_cgo__using_musl/ +* https://github.com/golang/go/issues/26492 diff --git a/docs/builds/windows-static-release.md b/docs/builds/windows-static-release.md new file mode 100644 index 0000000..c9a04b3 --- /dev/null +++ b/docs/builds/windows-static-release.md @@ -0,0 +1,16 @@ +# Windows Static Release Build + +You can build a statically-linked binary on Windows that should run +without requiring the SDL `*.dll` files. + +```sh +go build -x -v -trimpath -tags static -ldflags "-s -w -H windowsgui" +``` + +There is a script called [build-windows-static.sh][1] in the scripts +folder to make this easy from Git Bash. + +The Makefile supports cross-compiling for this from Linux with the +`cross_windows` target. + +[1]: scripts/build-windows-static.sh diff --git a/scripts/build-windows-386.sh b/scripts/build-windows-386.sh new file mode 100644 index 0000000..97e07b3 --- /dev/null +++ b/scripts/build-windows-386.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +. common-build-windows.sh + +build windows 386 diff --git a/scripts/build-windows-am64.sh b/scripts/build-windows-am64.sh new file mode 100644 index 0000000..b4da736 --- /dev/null +++ b/scripts/build-windows-am64.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +. common-release-build.sh + +build windows amd64 diff --git a/scripts/build-windows-static.sh b/scripts/build-windows-static.sh deleted file mode 100644 index 05ea7a4..0000000 --- a/scripts/build-windows-static.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env sh - -# This is intended to run in Git Bash on Windows. -# This builds a statically-linked release version of the binary ready to be deployed. - -OUTPUT=project-ely - -build(){ - OS=$1 - ARCH=$2 - - CGO_ENABLED=1 \ - CC=gcc \ - GOOS=$OS \ - GOARCH=$ARCH \ - go build -x -v -trimpath -tags static -ldflags="-s -w -H windowsgui" -o "${OUTPUT}-${OS}-${ARCH}.exe" -} - -build windows amd64 diff --git a/scripts/common-release-build.sh b/scripts/common-release-build.sh new file mode 100644 index 0000000..e831f78 --- /dev/null +++ b/scripts/common-release-build.sh @@ -0,0 +1,38 @@ +# This is a generalized function to build a statically-linked release binary. +# It specifies all the Go flags needed to make that happen depending on the platform. + +build(){ + OS=$1 + ARCH=$2 + + if [ -z $NAME ] + then + NAME=output + fi + + if [ -z "${CC}" ] + then + export CC=gcc + fi + + LDFLAGS_WIN="" + EXE="" + if [ "${OS}" == "windows" ] + then + # Hide the windows console popup for SDL + LDFLAGS_WIN="-H windowsgui" + EXE=".exe" + fi + + OUTPUT="${NAME}-${OS}-${ARCH}${EXE}" + + export CGO_ENABLED=1 + export CC="${CC}" + export GOOS=$OS + export GOARCH=$ARCH + go build -x -v \ + -trimpath \ + -tags static \ + -ldflags="-s -w ${LDFLAGS_WIN}" \ + -o "${OUTPUT}" +}