Skip to content

Commit

Permalink
Add support for Go build flags
Browse files Browse the repository at this point in the history
There are use cases, where multiple Go build flags need to be set. However, the
environment variable to pass flags to Go build has some limits for `ldFlags`.

Add GoReleaser inspired configuration section to `.ko.yaml` to support setting
specific Go build and ldFlags to be used by the build.

Add support for environment variables `KO_GOBUILD_FLAGS` to add extra build
flags and `KO_GOBUILD_LDFLAGS` to add extra ldFlags.
  • Loading branch information
HeavyWombat committed Apr 19, 2021
1 parent d498734 commit 7d6437a
Show file tree
Hide file tree
Showing 601 changed files with 34,181 additions and 4,791 deletions.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,31 @@ baseImageOverrides:
github.com/my-user/my-repo/cmd/foo: registry.example.com/base/for/foo
```
### Overriding Go build settings
By default, `ko` builds the binary with no additional arguments other than `--trimpath` (if available) and the ones required to name the binary accordingly. You can extend the `go build` arguments by providing extra build flags using a [GoReleaser](https://github.com/goreleaser/goreleaser) influenced `builds` configuration section in your `.ko.yaml`.

```yaml
builds:
- id: foo
dir: ./foobar/foo
flags:
- -tags
- netgo
ldflags:
- -s -w
- -extldflags "-static"
- id: bar
main: ./foobar/bar/main.go
ldflags:
- -s
- -w
```

For the build, `ko` will pick the entry based on the respective import path being used. It will be matched against the local path that is configured using `dir` and `main`. In the context of `ko`, it is fine just to specify `dir` with the intended import path.

_Please note:_ Even though the configuration section is similar to the [GoReleaser `builds` section](https://goreleaser.com/customization/build/), only the `flags` and `ldflags` fields are currently supported.

## Naming Images

`ko` provides a few different strategies for naming the image it pushes, to
Expand Down Expand Up @@ -324,6 +349,18 @@ instead:
GOFLAGS="-ldflags=-X=main.version=1.2.3" ko publish .
```

## How can I set multiple `ldflags`?

Currently, there is a limitation that does not allow to set multiple arguments in `ldflags` using `GOFLAGS`. Using `-ldflags` multiple times also does not work. For this, `ko` supports two ways to have more complex Go build setups:

- Using [`builds` section](#overriding-go-build-settings) in `.ko.yaml`.
- Using environment variables `KO_GOBUILD_FLAGS` and `KO_GOBUILD_LDFLAGS`.

```sh
export KO_GOBUILD_LDFLAGS="-X main.buildVersion=v0.1.0 -X main.buildCommit=123456AF -X main.buildTime=2021-04-06T11:53:52Z"
ko publish .
```

## Why are my images all created in 1970?

In order to support [reproducible builds](https://reproducible-builds.org), `ko`
Expand Down
10 changes: 4 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,18 @@ require (
github.com/go-training/helloworld v0.0.0-20200225145412-ba5f4379d78b
github.com/google/go-cmp v0.5.4
github.com/google/go-containerregistry v0.4.1-0.20210216200643-d81088d9983e
github.com/goreleaser/goreleaser v0.162.1
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect
github.com/mattmoor/dep-notify v0.0.0-20190205035814-a45dec370a17
github.com/onsi/gomega v1.10.3 // indirect
github.com/spf13/cobra v1.0.0
github.com/spf13/cobra v1.1.3
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.4.0
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
github.com/spf13/viper v1.7.1
golang.org/x/mod v0.4.1 // indirect
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65 // indirect
golang.org/x/tools v0.1.0
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gotest.tools/v3 v3.0.2 // indirect
k8s.io/apimachinery v0.19.7
k8s.io/cli-runtime v0.19.7
Expand Down
441 changes: 441 additions & 0 deletions go.sum

Large diffs are not rendered by default.

48 changes: 40 additions & 8 deletions pkg/build/gobuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
goreleaserCfg "github.com/goreleaser/goreleaser/pkg/config"
)

const (
Expand All @@ -64,7 +65,7 @@ For more information see:
// GetBase takes an importpath and returns a base image.
type GetBase func(context.Context, string) (Result, error)

type builder func(context.Context, string, v1.Platform, bool) (string, error)
type builder func(context.Context, string, v1.Platform, []string) (string, error)

type buildContext interface {
Import(path string, srcDir string, mode gb.ImportMode) (*gb.Package, error)
Expand All @@ -80,6 +81,7 @@ type gobuild struct {
creationTime v1.Time
build builder
disableOptimizations bool
buildCfgOverrides map[string]goreleaserCfg.Build
mod *modules
buildContext buildContext
platformMatcher *platformMatcher
Expand All @@ -94,6 +96,7 @@ type gobuildOpener struct {
creationTime v1.Time
build builder
disableOptimizations bool
buildCfgOverrides map[string]goreleaserCfg.Build
mod *modules
buildContext buildContext
platform string
Expand All @@ -113,6 +116,7 @@ func (gbo *gobuildOpener) Open() (Interface, error) {
creationTime: gbo.creationTime,
build: gbo.build,
disableOptimizations: gbo.disableOptimizations,
buildCfgOverrides: gbo.buildCfgOverrides,
mod: gbo.mod,
buildContext: gbo.buildContext,
labels: gbo.labels,
Expand Down Expand Up @@ -303,19 +307,16 @@ func platformToString(p v1.Platform) string {
return fmt.Sprintf("%s/%s", p.OS, p.Architecture)
}

func build(ctx context.Context, ip string, platform v1.Platform, disableOptimizations bool) (string, error) {
func build(ctx context.Context, ip string, platform v1.Platform, buildArgs []string) (string, error) {
tmpDir, err := ioutil.TempDir("", "ko")
if err != nil {
return "", err
}
file := filepath.Join(tmpDir, "out")

args := make([]string, 0, 7)
args := make([]string, 0, 5+len(buildArgs))
args = append(args, "build")
if disableOptimizations {
// Disable optimizations (-N) and inlining (-l).
args = append(args, "-gcflags", "all=-N -l")
}
args = append(args, buildArgs...)
args = append(args, "-o", file)
args = addGo113TrimPathFlag(args)
args = append(args, ip)
Expand Down Expand Up @@ -520,6 +521,37 @@ func (g *gobuild) tarKoData(ref reference) (*bytes.Buffer, error) {
return buf, walkRecursive(tw, root, kodataRoot)
}

func (g *gobuild) buildArgs(ip string) []string {
var args []string

if g.disableOptimizations {
// Disable optimizations (-N) and inlining (-l).
args = append(args, "-gcflags", "all=-N -l")
}

var buildFlags []string
if additionalBuildFlags, ok := os.LookupEnv("KO_GOBUILD_FLAGS"); ok {
buildFlags = append(buildFlags, strings.Split(additionalBuildFlags, " ")...)
}

var buildLdFlags []string
if additionalBuildLdFlags, ok := os.LookupEnv("KO_GOBUILD_LDFLAGS"); ok {
buildLdFlags = append(buildLdFlags, strings.Split(additionalBuildLdFlags, " ")...)
}

if buildCfg, ok := g.buildCfgOverrides[ip]; ok {
buildFlags = append(buildFlags, buildCfg.Flags...)
buildLdFlags = append(buildLdFlags, buildCfg.Ldflags...)
}

args = append(args, buildFlags...)
if len(buildLdFlags) > 0 {
args = append(args, fmt.Sprintf("-ldflags=%s", strings.Join(buildLdFlags, " ")))
}

return args
}

func (g *gobuild) buildOne(ctx context.Context, s string, base v1.Image, platform *v1.Platform) (v1.Image, error) {
ref := newRef(s)

Expand All @@ -536,7 +568,7 @@ func (g *gobuild) buildOne(ctx context.Context, s string, base v1.Image, platfor
}

// Do the build into a temporary file.
file, err := g.build(ctx, ref.Path(), *platform, g.disableOptimizations)
file, err := g.build(ctx, ref.Path(), *platform, g.buildArgs(ref.Path()))
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/build/gobuild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func TestGoBuildIsSupportedRefWithModules(t *testing.T) {
}

// A helper method we use to substitute for the default "build" method.
func writeTempFile(_ context.Context, s string, _ v1.Platform, _ bool) (string, error) {
func writeTempFile(_ context.Context, s string, _ v1.Platform, _ []string) (string, error) {
tmpDir, err := ioutil.TempDir("", "ko")
if err != nil {
return "", err
Expand Down
8 changes: 8 additions & 0 deletions pkg/build/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package build

import (
v1 "github.com/google/go-containerregistry/pkg/v1"
goreleaserCfg "github.com/goreleaser/goreleaser/pkg/config"
)

// WithBaseImages is a functional option for overriding the base images
Expand Down Expand Up @@ -45,6 +46,13 @@ func WithDisabledOptimizations() Option {
}
}

func WithGoReleaserBuildConfig(buildCfgOverrides map[string]goreleaserCfg.Build) Option {
return func(gbo *gobuildOpener) error {
gbo.buildCfgOverrides = buildCfgOverrides
return nil
}
}

// WithPlatforms is a functional option for building certain platforms for
// multi-platform base images. To build everything from the base, use "all",
// otherwise use a comma-separated list of platform specs, i.e.:
Expand Down
29 changes: 29 additions & 0 deletions pkg/commands/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"log"
"os"
"os/signal"
"path/filepath"
"strconv"
"strings"
"syscall"
Expand All @@ -31,12 +32,14 @@ import (
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/google/ko/pkg/build"
goreleaserCfg "github.com/goreleaser/goreleaser/pkg/config"
"github.com/spf13/viper"
)

var (
defaultBaseImage name.Reference
baseImageOverrides map[string]name.Reference
buildCfgOverrides map[string]goreleaserCfg.Build
)

func getBaseImage(platform string) build.GetBase {
Expand Down Expand Up @@ -160,4 +163,30 @@ func init() {
}
baseImageOverrides[k] = bi
}

var builds []goreleaserCfg.Build
if err := viper.UnmarshalKey("builds", &builds); err != nil {
log.Fatalf("configuration section 'builds' cannot be parsed")
}

buildCfgOverrides = make(map[string]goreleaserCfg.Build)
for _, build := range builds {
path := build.Dir
if len(path) == 0 {
path = "./"
}

if len(build.Main) > 0 {
if mainDir := filepath.Dir(build.Main); mainDir != "." {
path = path + string(filepath.Separator) + mainDir
}
}

importPath, err := qualifyLocalImport(path)
if err != nil {
log.Fatalf("failed to create qualified import path using path %s", path)
}

buildCfgOverrides[importPath] = build
}
}
5 changes: 5 additions & 0 deletions pkg/commands/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ func gobuildOptions(bo *options.BuildOptions) ([]build.Option, error) {
}
opts = append(opts, build.WithLabel(parts[0], parts[1]))
}

if len(buildCfgOverrides) > 0 {
opts = append(opts, build.WithGoReleaserBuildConfig(buildCfgOverrides))
}

return opts, nil
}

Expand Down
4 changes: 2 additions & 2 deletions vendor/github.com/Microsoft/go-winio/go.mod

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions vendor/github.com/Microsoft/go-winio/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 14 additions & 7 deletions vendor/github.com/Microsoft/go-winio/pipe.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vendor/github.com/apex/log/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 7d6437a

Please sign in to comment.