go编译生成更小的执行程序


原文链接: go编译生成更小的执行程序

go build -ldflags "-X github.com/spellgen/ldflagsx/cmd.Version=2.0"

1. 编译最小化可执行文件

尽管go1.7.3编译生成的可执行程序已经很小了,但是通过编译参数控制还能编译出更小的可执行文件,总结如下,
加-ldflags参数

在程序编译的时候可以加上-ldflags "-s -w" 参数来优化编译程序, 其实通过去除部分连接和调试等信息来使得编译之后的执行程序更小,具体参数如下:

-a 强制编译所有依赖包
-s 去掉符号表信息, panic时候的stack trace就没有任何文件名/行号信息了.
-w 去掉DWARF调试信息,得到的程序就不能用gdb调试了

如执行如下命令可得到优化编译过的test可执行程序:4.8M

$ go build -ldflags -w test.go

不建议s和w同时使用。

编译Dockerfile 使用的二进制文件

CGO_ENABLED=0 go build -a -tags netgo -ldflags '-s -w'

2. 压缩 使用upx

上面golang build 时加上-ldflags参数得到了比较小的可执行程序, 但是还可以通过upx这个开源,绿色,好用的压缩工具进行进一步压缩,首先其下载地址:http://upx.sourceforge.net/#downloadupx

linux 系统选择下载:upx-3.91-amd64_linux.tar.bz2 即可, 然后 tar -xvf upx-3.91-amd64_linux.tar.bz2 进入到解压文件夹中将可执行文件upx 拷贝到/usr/bin就可以全局执行了

$ sudo mv upx /usr/bin
upx -9 -k test

                   Ultimate Packer for eXecutables
                      Copyright (C) 1996 - 2013

UPX 3.91 Markus Oberhumer, Laszlo Molnar & John Reiser Sep 30th 2013

    File size         Ratio      Format      Name

5024059 -> 1552060 30.89% linux/ElfAMD test
Packed 1 file.

可以看到通过upx进一步压缩之后得到的程序大约只有1.5M了, 压缩比率达到了30.89%.
其中-k参数表示保留备份文件, -9代表最优压缩,upx总共有9档压缩,从1到9 其中 -1压缩速度最快.

再来看看下面编译之后的效果

$ go build test.go ---> 得到的 test 大约6.5MB
$ go build -ldflags -w ---> 得到的 test 大约4.8MB
$ upx -9 test ---> 得到的 test 大约 1.5MB

编译时,加入需要的版本号信息

有时候需要为每个编译都打上标记,不然真的很乱啊

演示用的golang代码

package main
var _VERSION_ = "unknown"
func main() {
	print("http_su ver=" + _VERSION_ + "\n")
}

编译时,加入需要的版本号信息,而不是直接去改main.go的源码

export TAG=v1.b.50
go build -ldflags "-X main.VERSION '$TAG'"

运行结果:

package main

import (
        "fmt"
)

var (
        Version string
        Build   string
        Entry   string

        funcs = map[string]func() {
                "f1":functionOne,
                "f2":functionTwo,
        }

)

func functionOne() {
    fmt.Println("This is function one")
}

func functionTwo() {
    fmt.Println("This is function two")
}

func main() {

        fmt.Println("Version: ", Version)
        fmt.Println("Build Time: ", Build)

    funcs[Entry]()

}
# This how we want to name the binary output
BINARY=gomake

# These are the values we want to pass for VERSION and BUILD
# git tag 1.0.1
# git commit -am "One more change after the tags"
VERSION=`git describe --tags`
BUILD=`date +%FT%T%z`

# Setup the -ldflags option for go build here, interpolate the variable values
LDFLAGS_f1=-ldflags "-w -s -X main.Version=${VERSION} -X main.Build=${BUILD} -X main.Entry=f1"
LDFLAGS_f2=-ldflags "-w -s -X main.Version=${VERSION} -X main.Build=${BUILD} -X main.Entry=f2"

# Builds the project
build:
    go build ${LDFLAGS_f1} -o ${BINARY}_f1
    go build ${LDFLAGS_f2} -o ${BINARY}_f2

# Installs our project: copies binaries
install:
    go install ${LDFLAGS_f1}

# Cleans our project: deletes binaries
clean:
    if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi

.PHONY: clean install

可以看到, 版本号根据编译参数的变化而变化了. 关键点是, 必须是 $package.$varName

package main

import "fmt"

var (
    version string
    date    string
)

func main() {
    fmt.Printf("version=%s, date=%s", version, date)
}

You can build using

go build -ldflags "-X main.version=0.0.1 -X main.date=%date:~10,4%-%date:~4,2%-%date:~7,2%T%time:~0,2%:%time:~3,2%:%time:~6,2%"

Date format assumes your environment echo %date% is Fri 07/22/2016 and echo %time% is 16:21:52.88

Then the output will be: version=0.0.1, date=2016-07-22T16:21:52

`