diff --git a/Makefile b/Makefile index 72ce0d955..2f87f0e7e 100644 --- a/Makefile +++ b/Makefile @@ -65,6 +65,12 @@ LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-cli/pkg/buildinfo.Date=$(BUILD_DAT LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-cli/pkg/buildinfo.SHA=$(BUILD_SHA)' LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-cli/pkg/buildinfo.Version=$(BUILD_VERSION)' +# Remove debug symbols to reduce binary size +# To build with debug symbols: TANZU_CLI_ENABLE_DEBUG=1 +ifneq ($(strip $(TANZU_CLI_ENABLE_DEBUG)),1) +LD_FLAGS += -w -s +endif + APT_IMAGE=ubuntu ifdef APT_BUILDER_IMAGE APT_IMAGE=$(APT_BUILDER_IMAGE) @@ -133,9 +139,9 @@ build-cli-%: ##Build the Tanzu Core CLI for a platform fi @if [ "$(OS)" = "windows" ]; then \ - GOOS=$(OS) GOARCH=$(ARCH) $(GO) build --ldflags "$(LD_FLAGS)" -o "$(ARTIFACTS_DIR)/$(OS)/$(ARCH)/cli/core/$(BUILD_VERSION)/tanzu-cli-$(OS)_$(ARCH).exe" ./cmd/tanzu/main.go;\ + GOOS=$(OS) GOARCH=$(ARCH) $(GO) build -gcflags=all="-l" --ldflags "$(LD_FLAGS)" -o "$(ARTIFACTS_DIR)/$(OS)/$(ARCH)/cli/core/$(BUILD_VERSION)/tanzu-cli-$(OS)_$(ARCH).exe" ./cmd/tanzu/main.go;\ else \ - GOOS=$(OS) GOARCH=$(ARCH) $(GO) build --ldflags "$(LD_FLAGS)" -o "$(ARTIFACTS_DIR)/$(OS)/$(ARCH)/cli/core/$(BUILD_VERSION)/tanzu-cli-$(OS)_$(ARCH)" ./cmd/tanzu/main.go;\ + GOOS=$(OS) GOARCH=$(ARCH) $(GO) build -gcflags=all="-l" --ldflags "$(LD_FLAGS)" -o "$(ARTIFACTS_DIR)/$(OS)/$(ARCH)/cli/core/$(BUILD_VERSION)/tanzu-cli-$(OS)_$(ARCH)" ./cmd/tanzu/main.go;\ fi ## -------------------------------------- diff --git a/cmd/plugin/builder/command/cli_compile.go b/cmd/plugin/builder/command/cli_compile.go index 4291519f0..49da6ba7c 100644 --- a/cmd/plugin/builder/command/cli_compile.go +++ b/cmd/plugin/builder/command/cli_compile.go @@ -69,6 +69,7 @@ type PluginCompileArgs struct { PluginScopeAssociationFile string TargetArch []string GroupByOSArch bool + DebugSymbols bool } const local = "local" @@ -103,6 +104,20 @@ func setGlobals(compileArgs *PluginCompileArgs) { // Append version specific ldflag by default so that user doesn't need to pass this ldflag always. ldflags = fmt.Sprintf("%s -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.Version=%s'", ldflags, version) + + // Remove debug symbols to reduce binary size + if !compileArgs.DebugSymbols { + ldflags = fmt.Sprintf("%s -w -s", ldflags) + } + + // Disable function inlining to reduce binary size + disableInlining := "-gcflags=all=-l" + if len(goflags) > 0 { + // Append the user-defined goflags so they can override the default if needed + goflags = fmt.Sprintf("%s %s", disableInlining, goflags) + } else { + goflags = disableInlining + } } func Compile(compileArgs *PluginCompileArgs) error { diff --git a/cmd/plugin/builder/plugin.go b/cmd/plugin/builder/plugin.go index 1ddf1c6a7..cfa584ce9 100644 --- a/cmd/plugin/builder/plugin.go +++ b/cmd/plugin/builder/plugin.go @@ -39,6 +39,7 @@ type pluginBuildFlags struct { Match string PluginScopeAssociationFile string GoFlags string + DebugSymbols bool } type pluginBuildPackageFlags struct { @@ -82,6 +83,7 @@ func newPluginBuildCmd() *cobra.Command { PluginScopeAssociationFile: pbFlags.PluginScopeAssociationFile, GroupByOSArch: true, GoFlags: pbFlags.GoFlags, + DebugSymbols: pbFlags.DebugSymbols, } return command.Compile(compileArgs) @@ -96,6 +98,7 @@ func newPluginBuildCmd() *cobra.Command { pluginBuildCmd.Flags().StringVarP(&pbFlags.Match, "match", "", "*", "match a plugin name to build, supports globbing") pluginBuildCmd.Flags().StringVarP(&pbFlags.PluginScopeAssociationFile, "plugin-scope-association-file", "", "", "file specifying plugin scope association") pluginBuildCmd.Flags().StringVarP(&pbFlags.GoFlags, "goflags", "", "", "goflags to set on build") + pluginBuildCmd.Flags().BoolVarP(&pbFlags.DebugSymbols, "debug-symbols", "", false, "include debug symbols in the build") _ = pluginBuildCmd.MarkFlagRequired("version") diff --git a/cmd/plugin/builder/template/plugintemplates/plugin-tooling.mk.tmpl b/cmd/plugin/builder/template/plugintemplates/plugin-tooling.mk.tmpl index 3ce275ee5..d9d8e6d8b 100644 --- a/cmd/plugin/builder/template/plugintemplates/plugin-tooling.mk.tmpl +++ b/cmd/plugin/builder/template/plugintemplates/plugin-tooling.mk.tmpl @@ -20,8 +20,21 @@ endif PLUGIN_LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.Date=$(PLUGIN_BUILD_DATE)' PLUGIN_LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.SHA=$(PLUGIN_BUILD_SHA)' PLUGIN_LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.Version=$(PLUGIN_BUILD_VERSION)' + +# This variable can be used to pass additional go flags to the build command. +# Note that the builder plugin already implicitly sets the go flags "-gcflags=all=-l" +# which disable function inlining to reduce binary size. If you want to prevent the +# use of this flag, you can set PLUGIN_GO_FLAGS to "-gcflags=all=" which will replace +# the default value. PLUGIN_GO_FLAGS ?= +# To build with debug symbols use PLUGIN_ENABLE_DEBUG=1 +ifeq ($(strip $(PLUGIN_ENABLE_DEBUG)),1) +PLUGIN_DEBUG=true +else +PLUGIN_DEBUG=false +endif + # Add supported OS-ARCHITECTURE combinations here PLUGIN_BUILD_OS_ARCH ?= linux-amd64 windows-amd64 darwin-amd64 darwin-arm64 linux-arm64 @@ -102,7 +115,8 @@ plugin-build-%: --goflags "$(PLUGIN_GO_FLAGS)" \ --os-arch $(OS)_$(ARCH) \ --match "$(PLUGIN_NAME)" \ - --plugin-scope-association-file $(PLUGIN_SCOPE_ASSOCIATION_FILE) + --plugin-scope-association-file $(PLUGIN_SCOPE_ASSOCIATION_FILE) \ + --debug-symbols=$(PLUGIN_DEBUG) .PHONY: plugin-build-packages plugin-build-packages: ## Build plugin packages diff --git a/docs/dev/README.md b/docs/dev/README.md index 6191d3064..8b27a8bf4 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -55,6 +55,17 @@ To run e2e tests for the repository: make e2e-cli-core ``` +### Debugging + +By default the CLI is built without debug symbols to reduce its binary size; +this has the side-effect of preventing the use of a debugger towards the built +binary. However, when using an IDE, a different binary is used, one built by +the IDE, and therefore the debugger can be used directly from the IDE. + +If you require using a debugger directly on a CLI binary, you have to build +a binary that includes the debug symbols. You can build such a binary by using +`TANZU_CLI_ENABLE_DEBUG=1` along with your build command. + ## Centralized Discovery of Plugins The Tanzu CLI uses a system of plugins to provide functionality to interact diff --git a/docs/plugindev/README.md b/docs/plugindev/README.md index 7cf26490e..9fbbdd866 100644 --- a/docs/plugindev/README.md +++ b/docs/plugindev/README.md @@ -231,6 +231,30 @@ well. Edit the file as appropriate. +#### Optimizations, debugging and build flags + +By default the `builder` plugin `v1.2.0` or later optimizes build flags to +reduce the binary size of the plugin being built. Two optimizations are done: +building without debug symbols (which reduces the binary size by up to 30%), +and deactivating function inlining (which reduces the binary size by about 6%). + +Building without debug symbols has the side-effect of preventing the use of a +debugger towards the built binary. However, when using an IDE, a different +binary is used, one built by the IDE, and therefore the debugger can be used +directly from the IDE. + +If you require using a debugger directly on a plugin binary, you have to build +a binary that includes the debug symbols. You can build such a binary by using +`PLUGIN_ENABLE_DEBUG=1` along with your `make` command. + +The `builder` plugin deactivates function inlining by implicitly injecting +the `-gcflags=all=-l` go flags. Be aware that if you use the `PLUGIN_GO_FLAGS` +variable to inject other `-gcflags`, you should pay attention to also include +the `all=-l` to keep the optimization. If for some reason you need to activate +function inlining (at the cost of a larger binary), you can do so by +using `PLUGIN_GO_FLAGS=-gcflags=all=` (without the `-l` specified), which will +have the effect of replacing the go flags specified by the `builder` plugin. + ### Publishing a plugin To publish one or more built plugins to a target repository, one would need to diff --git a/plugin-tooling.mk b/plugin-tooling.mk index 0ffa1908c..8ea0e06fa 100644 --- a/plugin-tooling.mk +++ b/plugin-tooling.mk @@ -24,8 +24,21 @@ endif PLUGIN_LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.Date=$(PLUGIN_BUILD_DATE)' PLUGIN_LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.SHA=$(PLUGIN_BUILD_SHA)' PLUGIN_LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.Version=$(PLUGIN_BUILD_VERSION)' + +# This variable can be used to pass additional go flags to the build command. +# Note that the builder plugin already implicitly sets the go flags "-gcflags=all=-l" +# which disable function inlining to reduce binary size. If you want to prevent the +# use of this flag, you can set PLUGIN_GO_FLAGS to "-gcflags=all=" which will replace +# the default value. PLUGIN_GO_FLAGS ?= +# To build with debug symbols use PLUGIN_ENABLE_DEBUG=1 +ifeq ($(strip $(PLUGIN_ENABLE_DEBUG)),1) +PLUGIN_DEBUG=true +else +PLUGIN_DEBUG=false +endif + # Add supported OS-ARCHITECTURE combinations here PLUGIN_BUILD_OS_ARCH ?= linux-amd64 windows-amd64 darwin-amd64 darwin-arm64 linux-arm64 @@ -106,7 +119,8 @@ plugin-build-%: --goflags "$(PLUGIN_GO_FLAGS)" \ --os-arch $(OS)_$(ARCH) \ --match "$(PLUGIN_NAME)" \ - --plugin-scope-association-file $(PLUGIN_SCOPE_ASSOCIATION_FILE) + --plugin-scope-association-file $(PLUGIN_SCOPE_ASSOCIATION_FILE) \ + --debug-symbols=$(PLUGIN_DEBUG) .PHONY: plugin-build-packages plugin-build-packages: ## Build plugin packages diff --git a/test/sample-plugin/Makefile b/test/sample-plugin/Makefile index 846a901c6..f232491df 100644 --- a/test/sample-plugin/Makefile +++ b/test/sample-plugin/Makefile @@ -37,9 +37,11 @@ $(GOLANGCI_LINT): $(TOOLS_BIN_DIR) ## Install golangci-lint .PHONY: install-builder install-builder: ## Install builder + $(MAKE) -C $(ROOT_DIR_RELATIVE)/../.. prepare-builder + unset BUILDER_PLUGIN ; \ export TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER="No" ; \ export TANZU_CLI_EULA_PROMPT_ANSWER="Yes" ; \ - tanzu plugin install builder + $(MAKE) -C $(ROOT_DIR_RELATIVE)/../.. plugin-build-install-local PLUGIN_NAME=builder .PHONEY: e2e-tests-simple-plugin ## Run all e2e tests for simple plugin e2e-tests-simple-plugin: install-builder plugin-build-local plugin-install-local e2e-tests-sample-plugin-functionality e2e-tests-sample-plugin-e2e-api diff --git a/test/sample-plugin/plugin-tooling.mk b/test/sample-plugin/plugin-tooling.mk index 61857a809..43cc4e06e 100644 --- a/test/sample-plugin/plugin-tooling.mk +++ b/test/sample-plugin/plugin-tooling.mk @@ -20,8 +20,21 @@ endif PLUGIN_LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.Date=$(PLUGIN_BUILD_DATE)' PLUGIN_LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.SHA=$(PLUGIN_BUILD_SHA)' PLUGIN_LD_FLAGS += -X 'github.com/vmware-tanzu/tanzu-plugin-runtime/plugin/buildinfo.Version=$(PLUGIN_BUILD_VERSION)' + +# This variable can be used to pass additional go flags to the build command. +# Note that the builder plugin already implicitly sets the go flags "-gcflags=all=-l" +# which disable function inlining to reduce binary size. If you want to prevent the +# use of this flag, you can set PLUGIN_GO_FLAGS to "-gcflags=all=" which will replace +# the default value. PLUGIN_GO_FLAGS ?= +# To build with debug symbols use PLUGIN_ENABLE_DEBUG=1 +ifeq ($(strip $(PLUGIN_ENABLE_DEBUG)),1) +PLUGIN_DEBUG=true +else +PLUGIN_DEBUG=false +endif + # Add supported OS-ARCHITECTURE combinations here PLUGIN_BUILD_OS_ARCH ?= linux-amd64 windows-amd64 darwin-amd64 darwin-arm64 linux-arm64 @@ -88,8 +101,9 @@ plugin-install-local: .PHONY: plugin-build plugin-build: $(PLUGIN_BUILD_TARGETS) generate-plugin-bundle ## Build all plugin binaries for all supported os-arch +.PHONY: plugin-build-local plugin-build-local: plugin-build-$(GOHOSTOS)-$(GOHOSTARCH) ## Build all plugin binaries for local platform - + plugin-build-%: $(eval ARCH = $(word 2,$(subst -, ,$*))) $(eval OS = $(word 1,$(subst -, ,$*))) @@ -101,7 +115,8 @@ plugin-build-%: --goflags "$(PLUGIN_GO_FLAGS)" \ --os-arch $(OS)_$(ARCH) \ --match "$(PLUGIN_NAME)" \ - --plugin-scope-association-file $(PLUGIN_SCOPE_ASSOCIATION_FILE) + --plugin-scope-association-file $(PLUGIN_SCOPE_ASSOCIATION_FILE) \ + --debug-symbols=$(PLUGIN_DEBUG) .PHONY: plugin-build-packages plugin-build-packages: ## Build plugin packages