#+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 冲突。