Go 项目的静态编译方式

#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org

默认 go 语言是静态编译的, 比如:

$ cat hello.go && go build hello.go && file hello && ldd hello
package main
import "fmt"
func main () {
    fmt.Println("hello")
}

hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
        not a dynamic executable

但是对于使用了 CGO 机制直接用 C 代码的项目,默认情况下是动态链接的, 这在比如容器环境下调试非常不方便,所以最好采用静态链接的方式。在实践中,通常又分为两种方式:

自己的项目工程中使用了 CGO

例如以下情况:

$ cat hello1.go && go build hello1.go && file hello1 && ldd hello1
package main

//#include <stdio.h>
import "C"

func main() {
    C.puts(C.CString("Hello, Cgo\n"))
}
hello1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0cb254b1d81832db6f1685361a7e6c228a1bdae3, not stripped
        linux-vdso.so.1 =>  (0x00007ffe8f065000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb96dd1e000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fb96d950000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fb96df3a000)

在容器环境下调试代码的时候,外部编译好的程序由于依赖的关系, 在容器中会运行失败

$ cat hello1.go
package main

// #include <stdio.h>
// void hello() {printf("hello1\n");}
import "C"

func main() {
    C.hello()
}
$ go build -o /tmp/hello1 hello1.go && docker run -ti --rm -v /tmp:/data busybox /data/hello1
exec /data/hello1: no such file or directory

这个时候需要采用静态编译的方式来构建程序

$ go build -o /tmp/hello1 -ldflags '-extldflags "-static"' -a hello1.go && file hello1
hello1: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=be8ef4888e8e1d29787b32e0a6ad8064e60a931a, not stripped

$ docker run -ti --rm -v /tmp:/data busybox /data/hello1
hello1

其中:

  • -ldflags 表示传递给 Go 链接器的参数
  • -extldflags "-static" 告诉 Go 的链接器采用静态编译该程序

导入的标准库中使用了 CGO

目前标准库中有两个模块使用了 CGO:

  • os/user 有原生go实现和C实现。c实现的好处在于能在 LDAP等获取用户信息,一般用不到。
  • net 同样有两个实现,大部分情况下 go 的实现已经够用了。

因此,只要将上面两个模块使用 go 原生实现就可以达到静态编译的效果,这里有两种方式实现:

第一种,使用 -tags osusergo,netgo 明确使用 go 的实现

$ go build main.go && file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

$ go build -tags osusergo,netgo main.go && file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

第二种方式,使用 "CGO_ENABLED=0" 表示禁用 CGO

$ go build main.go && file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

$ CGO_ENABLED=0 go build main.go && file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

注意:这种方式和上面提到的自己的项目中使用了 CGO 冲突。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦