Skip to content

Commit

Permalink
build: add static linking support (ethereum#25492)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
2 people authored and blakehhuynh committed Oct 7, 2022
1 parent 6caca2e commit ef49fef
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.alltools
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 22 additions & 11 deletions build/ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
Expand All @@ -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")

Expand All @@ -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)
Expand All @@ -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
}

Expand Down
19 changes: 17 additions & 2 deletions internal/build/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"text/template"
"time"
Expand All @@ -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
Expand All @@ -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...))
}
Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit ef49fef

Please sign in to comment.