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

View File

@ -14,7 +14,7 @@ COPY go.sum /go-ethereum/
RUN cd /go-ethereum && go mod download
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
FROM alpine:latest

View File

@ -14,7 +14,7 @@ COPY go.sum /go-ethereum/
RUN cd /go-ethereum && go mod download
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
FROM alpine:latest

View File

@ -200,9 +200,10 @@ func main() {
func doInstall(cmdline []string) {
var (
dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
arch = flag.String("arch", "", "Architecture to cross build for")
cc = flag.String("cc", "", "C compiler to cross build with")
dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
arch = flag.String("arch", "", "Architecture to cross build for")
cc = flag.String("cc", "", "C compiler to cross build with")
staticlink = flag.Bool("static", false, "Create statically-linked executable")
)
flag.CommandLine.Parse(cmdline)
@ -213,9 +214,12 @@ func doInstall(cmdline []string) {
tc.Root = build.DownloadGo(csdb, dlgoVersion)
}
// Disable CLI markdown doc generation in release builds.
buildTags := []string{"urfave_cli_no_docs"}
// Configure the build.
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,
// 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")
}
// 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.
gobuild.Args = append(gobuild.Args, "-trimpath")
@ -251,7 +252,7 @@ func doInstall(cmdline []string) {
}
// 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
if 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" {
ld = append(ld, "-s")
}
// Enforce the stacksize to 8M, which is the case on most platforms apart from
// alpine Linux.
if runtime.GOOS == "linux" {
ld = append(ld, "-extldflags", "-Wl,-z,stack-size=0x800000")
// Enforce the stacksize to 8M, which is the case on most platforms apart from
// alpine Linux.
extld := []string{"-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 {
flags = append(flags, "-ldflags", strings.Join(ld, " "))
}
if len(buildTags) > 0 {
flags = append(flags, "-tags", strings.Join(buildTags, ","))
}
return flags
}

View File

@ -29,6 +29,7 @@ import (
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"text/template"
"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
// any error.
func MustRun(cmd *exec.Cmd) {
fmt.Println(">>>", strings.Join(cmd.Args, " "))
fmt.Println(">>>", printArgs(cmd.Args))
if !*DryRunFlag {
cmd.Stderr = os.Stderr
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) {
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, host)
fmt.Println(">>>", strings.Join(sftp.Args, " "))
fmt.Println(">>>", printArgs(sftp.Args))
if *DryRunFlag {
return nil
}