build: add static linking support (#25492)

This adds support for building statically-linked executables using ci.go.

Static linking is enabled by default in Docker builds, making it possible to
use the geth executable in any Docker image, regardless of the Linux
distribution the Dockerfile is based on.

Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
0xe3b0c4 2022-08-10 16:30:59 +08:00 committed by GitHub
parent c4cd632f47
commit c0cc6f6362
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 15 deletions

@ -14,7 +14,7 @@ COPY go.sum /go-ethereum/
RUN cd /go-ethereum && go mod download RUN cd /go-ethereum && go mod download
ADD . /go-ethereum ADD . /go-ethereum
RUN cd /go-ethereum && go run build/ci.go install ./cmd/geth RUN cd /go-ethereum && go run build/ci.go install -static ./cmd/geth
# Pull Geth into a second stage deploy alpine container # Pull Geth into a second stage deploy alpine container
FROM alpine:latest FROM alpine:latest

@ -14,7 +14,7 @@ COPY go.sum /go-ethereum/
RUN cd /go-ethereum && go mod download RUN cd /go-ethereum && go mod download
ADD . /go-ethereum ADD . /go-ethereum
RUN cd /go-ethereum && go run build/ci.go install RUN cd /go-ethereum && go run build/ci.go install -static
# Pull all binaries into a second stage deploy alpine container # Pull all binaries into a second stage deploy alpine container
FROM alpine:latest FROM alpine:latest

@ -203,6 +203,7 @@ func doInstall(cmdline []string) {
dlgo = flag.Bool("dlgo", false, "Download Go and build with it") dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
arch = flag.String("arch", "", "Architecture to cross build for") arch = flag.String("arch", "", "Architecture to cross build for")
cc = flag.String("cc", "", "C compiler to cross build with") cc = flag.String("cc", "", "C compiler to cross build with")
staticlink = flag.Bool("static", false, "Create statically-linked executable")
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
@ -213,9 +214,12 @@ func doInstall(cmdline []string) {
tc.Root = build.DownloadGo(csdb, dlgoVersion) tc.Root = build.DownloadGo(csdb, dlgoVersion)
} }
// Disable CLI markdown doc generation in release builds.
buildTags := []string{"urfave_cli_no_docs"}
// Configure the build. // Configure the build.
env := build.Env() env := build.Env()
gobuild := tc.Go("build", buildFlags(env)...) gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...)
// arm64 CI builders are memory-constrained and can't handle concurrent builds, // arm64 CI builders are memory-constrained and can't handle concurrent builds,
// better disable it. This check isn't the best, it should probably // better disable it. This check isn't the best, it should probably
@ -224,9 +228,6 @@ func doInstall(cmdline []string) {
gobuild.Args = append(gobuild.Args, "-p", "1") gobuild.Args = append(gobuild.Args, "-p", "1")
} }
// Disable CLI markdown doc generation in release builds.
gobuild.Args = append(gobuild.Args, "-tags", "urfave_cli_no_docs")
// We use -trimpath to avoid leaking local paths into the built executables. // We use -trimpath to avoid leaking local paths into the built executables.
gobuild.Args = append(gobuild.Args, "-trimpath") gobuild.Args = append(gobuild.Args, "-trimpath")
@ -251,7 +252,7 @@ func doInstall(cmdline []string) {
} }
// buildFlags returns the go tool flags for building. // buildFlags returns the go tool flags for building.
func buildFlags(env build.Environment) (flags []string) { func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
var ld []string var ld []string
if env.Commit != "" { if env.Commit != "" {
ld = append(ld, "-X", "main.gitCommit="+env.Commit) ld = append(ld, "-X", "main.gitCommit="+env.Commit)
@ -262,14 +263,24 @@ func buildFlags(env build.Environment) (flags []string) {
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
ld = append(ld, "-s") ld = append(ld, "-s")
} }
if runtime.GOOS == "linux" {
// Enforce the stacksize to 8M, which is the case on most platforms apart from // Enforce the stacksize to 8M, which is the case on most platforms apart from
// alpine Linux. // alpine Linux.
if runtime.GOOS == "linux" { extld := []string{"-Wl,-z,stack-size=0x800000"}
ld = append(ld, "-extldflags", "-Wl,-z,stack-size=0x800000") if staticLinking {
extld = append(extld, "-static")
// Under static linking, use of certain glibc features must be
// disabled to avoid shared library dependencies.
buildTags = append(buildTags, "osusergo", "netgo")
}
ld = append(ld, "-extldflags", "'"+strings.Join(extld, " ")+"'")
} }
if len(ld) > 0 { if len(ld) > 0 {
flags = append(flags, "-ldflags", strings.Join(ld, " ")) flags = append(flags, "-ldflags", strings.Join(ld, " "))
} }
if len(buildTags) > 0 {
flags = append(flags, "-tags", strings.Join(buildTags, ","))
}
return flags return flags
} }

@ -29,6 +29,7 @@ import (
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"text/template" "text/template"
"time" "time"
@ -39,7 +40,7 @@ var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands")
// MustRun executes the given command and exits the host process for // MustRun executes the given command and exits the host process for
// any error. // any error.
func MustRun(cmd *exec.Cmd) { func MustRun(cmd *exec.Cmd) {
fmt.Println(">>>", strings.Join(cmd.Args, " ")) fmt.Println(">>>", printArgs(cmd.Args))
if !*DryRunFlag { if !*DryRunFlag {
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@ -49,6 +50,20 @@ func MustRun(cmd *exec.Cmd) {
} }
} }
func printArgs(args []string) string {
var s strings.Builder
for i, arg := range args {
if i > 0 {
s.WriteByte(' ')
}
if strings.IndexByte(arg, ' ') >= 0 {
arg = strconv.QuoteToASCII(arg)
}
s.WriteString(arg)
}
return s.String()
}
func MustRunCommand(cmd string, args ...string) { func MustRunCommand(cmd string, args ...string) {
MustRun(exec.Command(cmd, args...)) MustRun(exec.Command(cmd, args...))
} }
@ -121,7 +136,7 @@ func UploadSFTP(identityFile, host, dir string, files []string) error {
sftp.Args = append(sftp.Args, "-i", identityFile) sftp.Args = append(sftp.Args, "-i", identityFile)
} }
sftp.Args = append(sftp.Args, host) sftp.Args = append(sftp.Args, host)
fmt.Println(">>>", strings.Join(sftp.Args, " ")) fmt.Println(">>>", printArgs(sftp.Args))
if *DryRunFlag { if *DryRunFlag {
return nil return nil
} }