Skip to content

Commit

Permalink
Add Trimpath build option
Browse files Browse the repository at this point in the history
Enables programmatic control of whether `ko` adds the `-trimpath`
flag to `go build`.

The `-trimpath` flag removes file system paths from the resulting
binary. `ko` adds `-trimpath` by default as it aids in achieving
reproducible builds.

However, removing file system paths makes interactive debugging more
challenging, in particular in mapping source file locations in the
IDE to debug information in the binary.

If you set `Trimpath` to `false` to enable interactive debugging, you
probably also want to set `DisableOptimizations` to `true` to disable
compiler optimizations and inlining.

Reference for `-trimpath`:
https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies

Resolves: #500
Related: #71, #78, GoogleContainerTools/skaffold#6843
  • Loading branch information
halvards committed Nov 17, 2021
1 parent 08fccaa commit 2fbc908
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 3 deletions.
10 changes: 7 additions & 3 deletions pkg/build/gobuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type gobuild struct {
kodataCreationTime v1.Time
build builder
disableOptimizations bool
trimpath bool
buildConfigs map[string]Config
platformMatcher *platformMatcher
dir string
Expand All @@ -80,6 +81,7 @@ type gobuildOpener struct {
kodataCreationTime v1.Time
build builder
disableOptimizations bool
trimpath bool
buildConfigs map[string]Config
platform string
labels map[string]string
Expand All @@ -100,6 +102,7 @@ func (gbo *gobuildOpener) Open() (Interface, error) {
kodataCreationTime: gbo.kodataCreationTime,
build: gbo.build,
disableOptimizations: gbo.disableOptimizations,
trimpath: gbo.trimpath,
buildConfigs: gbo.buildConfigs,
labels: gbo.labels,
dir: gbo.dir,
Expand Down Expand Up @@ -556,9 +559,10 @@ func createBuildArgs(buildCfg Config) ([]string, error) {
}

func (g *gobuild) configForImportPath(ip string) Config {
config, ok := g.buildConfigs[ip]
if !ok {
// Apply default build flags in case none were supplied
config := g.buildConfigs[ip]
if g.trimpath {
// The `-trimpath` flag removes file system paths from the resulting binary, to aid reproducibility.
// Ref: https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies
config.Flags = append(config.Flags, "-trimpath")
}

Expand Down
97 changes: 97 additions & 0 deletions pkg/build/gobuild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
Expand Down Expand Up @@ -290,6 +291,102 @@ func TestBuildEnv(t *testing.T) {
}
}

func TestBuildConfig(t *testing.T) {
tests := []struct {
description string
options []Option
importpath string
expectConfig Config
}{
{
description: "minimal options",
options: []Option{
WithBaseImages(nilGetBase),
},
},
{
description: "trimpath flag",
options: []Option{
WithBaseImages(nilGetBase),
WithTrimpath(true),
},
expectConfig: Config{
Flags: FlagArray{"-trimpath"},
},
},
{
description: "no trimpath flag",
options: []Option{
WithBaseImages(nilGetBase),
WithTrimpath(false),
},
},
{
description: "build config and trimpath",
options: []Option{
WithBaseImages(nilGetBase),
WithConfig(map[string]Config{
"example.com/foo": {
Flags: FlagArray{"-v"},
},
}),
WithTrimpath(true),
},
importpath: "example.com/foo",
expectConfig: Config{
Flags: FlagArray{"-v", "-trimpath"},
},
},
{
description: "no trimpath overridden by build config flag",
options: []Option{
WithBaseImages(nilGetBase),
WithConfig(map[string]Config{
"example.com/bar": {
Flags: FlagArray{"-trimpath"},
},
}),
WithTrimpath(false),
},
importpath: "example.com/bar",
expectConfig: Config{
Flags: FlagArray{"-trimpath"},
},
},
{
description: "disable optimizations",
options: []Option{
WithBaseImages(nilGetBase),
WithDisabledOptimizations(),
},
expectConfig: Config{
Flags: FlagArray{"-gcflags", "all=-N -l"},
},
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
i, err := NewGo(context.Background(), "", test.options...)
if err != nil {
t.Fatalf("NewGo(): unexpected error: %+v", err)
}
gb, ok := i.(*gobuild)
if !ok {
t.Fatal("NewGo() did not return *gobuild{} as expected")
}
config := gb.configForImportPath(test.importpath)
if diff := cmp.Diff(test.expectConfig, config, cmpopts.EquateEmpty(),
cmpopts.SortSlices(func(x, y string) bool { return x < y })); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expectConfig, diff)
}
})
}
}

func nilGetBase(_ context.Context, _ string) (name.Reference, Result, error) {
return nil, nil, nil
}

// A helper method we use to substitute for the default "build" method.
func writeTempFile(_ context.Context, s string, _ string, _ v1.Platform, _ Config) (string, error) {
tmpDir, err := ioutil.TempDir("", "ko")
Expand Down
9 changes: 9 additions & 0 deletions pkg/build/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ func WithDisabledOptimizations() Option {
}
}

// WithTrimpath is a functional option that controls whether the `-trimpath`
// flag is added to `go build`.
func WithTrimpath(v bool) Option {
return func(gbo *gobuildOpener) error {
gbo.trimpath = v
return nil
}
}

// WithConfig is a functional option for providing GoReleaser Build influenced
// build settings for importpaths.
//
Expand Down
7 changes: 7 additions & 0 deletions pkg/commands/options/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ type BuildOptions struct {

InsecureRegistry bool

// Trimpath controls whether ko adds the `-trimpath` flag to `go build` by default.
// The `-trimpath` flags aids in achieving reproducible builds, but it removes path information that is useful for interactive debugging.
// Set this field to `false` and `DisableOptimizations` to `true` if you want to interactively debug the binary in the resulting image.
// `AddBuildOptions()` defaults this field to `true`.
Trimpath bool

// BuildConfigs stores the per-image build config from `.ko.yaml`.
BuildConfigs map[string]build.Config
}
Expand All @@ -71,6 +77,7 @@ func AddBuildOptions(cmd *cobra.Command, bo *BuildOptions) {
"Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*")
cmd.Flags().StringSliceVar(&bo.Labels, "image-label", []string{},
"Which labels (key=value) to add to the image.")
bo.Trimpath = true
}

// LoadConfig reads build configuration from defaults, environment variables, and the `.ko.yaml` config file.
Expand Down
10 changes: 10 additions & 0 deletions pkg/commands/options/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"testing"

"github.com/google/ko/pkg/build"
"github.com/spf13/cobra"
)

func TestDefaultBaseImage(t *testing.T) {
Expand Down Expand Up @@ -87,3 +88,12 @@ func TestCreateBuildConfigs(t *testing.T) {
}
}
}

func TestAddBuildOptionsSetsDefaultsForNonFlagOptions(t *testing.T) {
cmd := &cobra.Command{}
bo := &BuildOptions{}
AddBuildOptions(cmd, bo)
if !bo.Trimpath {
t.Error("expected Trimpath=true")
}
}
1 change: 1 addition & 0 deletions pkg/commands/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ func gobuildOptions(bo *options.BuildOptions) ([]build.Option, error) {
if bo.DisableOptimizations {
opts = append(opts, build.WithDisabledOptimizations())
}
opts = append(opts, build.WithTrimpath(bo.Trimpath))
for _, lf := range bo.Labels {
parts := strings.SplitN(lf, "=", 2)
if len(parts) != 2 {
Expand Down

0 comments on commit 2fbc908

Please sign in to comment.